diff --git a/client/constants.js b/client/constants.js
index cf1ef18431..5f04e83980 100644
--- a/client/constants.js
+++ b/client/constants.js
@@ -33,8 +33,13 @@ export const RESET_PROJECT = 'RESET_PROJECT';
export const SET_PROJECT = 'SET_PROJECT';
export const SET_PROJECTS = 'SET_PROJECTS';
+// <<<<<<< HEAD
+export const SET_COLLECTIONS_FOR_COLLECTION_LIST =
+ 'SET_COLLECTIONS_FOR_COLLECTION_LIST';
+// =======
export const SET_PROJECTS_FOR_COLLECTION_LIST =
'SET_PROJECTS_FOR_COLLECTION_LIST';
+// >>>>>>> develop-codemirror-v6
export const SET_COLLECTIONS = 'SET_COLLECTIONS';
export const CREATE_COLLECTION = 'CREATED_COLLECTION';
diff --git a/client/modules/IDE/actions/collections.js b/client/modules/IDE/actions/collections.js
index 68a42f500d..3f2049d307 100644
--- a/client/modules/IDE/actions/collections.js
+++ b/client/modules/IDE/actions/collections.js
@@ -6,33 +6,33 @@ import { setToastText, showToast } from './toast';
const TOAST_DISPLAY_TIME_MS = 1500;
-export function getCollections(username) {
- return (dispatch) => {
- dispatch(startLoader());
- let url;
- if (username) {
- url = `/${username}/collections`;
- } else {
- url = '/collections';
- }
- return apiClient
- .get(url)
- .then((response) => {
- dispatch({
- type: ActionTypes.SET_COLLECTIONS,
- collections: response.data
- });
- dispatch(stopLoader());
- })
- .catch((error) => {
- dispatch({
- type: ActionTypes.ERROR,
- error: error?.response?.data
- });
- dispatch(stopLoader());
- });
- };
-}
+// export function getCollections(username) {
+// return (dispatch) => {
+// dispatch(startLoader());
+// let url;
+// if (username) {
+// url = `/${username}/collections`;
+// } else {
+// url = '/collections';
+// }
+// return apiClient
+// .get(url)
+// .then((response) => {
+// dispatch({
+// type: ActionTypes.SET_COLLECTIONS,
+// collections: response.data
+// });
+// dispatch(stopLoader());
+// })
+// .catch((error) => {
+// dispatch({
+// type: ActionTypes.ERROR,
+// error: error?.response?.data
+// });
+// dispatch(stopLoader());
+// });
+// };
+// }
export function createCollection(collection) {
return (dispatch) => {
@@ -167,3 +167,60 @@ export function deleteCollection(collectionId) {
});
};
}
+const buildCollectionUrl = (username, options = {}) => {
+ const {
+ page = 1,
+ limit = 10,
+ sortField = 'updatedAt',
+ sortDir = 'desc',
+ q = ''
+ } = options;
+
+ const base = username
+ ? `/${encodeURIComponent(username)}/collections`
+ : '/collections';
+
+ const params = new URLSearchParams({
+ page: String(page),
+ limit: String(limit),
+ sortField,
+ sortDir
+ });
+
+ const trimmed = q.trim();
+
+ if (trimmed) {
+ params.set('q', trimmed);
+ }
+
+ return `${base}?${params.toString()}`;
+};
+
+const fetchCollections = (username, options, successType) => (dispatch) => {
+ dispatch(startLoader());
+
+ const url = buildCollectionUrl(username, options);
+
+ return apiClient
+ .get(url)
+ .then((response) => {
+ dispatch({ type: successType, collections: response.data });
+ dispatch(stopLoader());
+ return response.data;
+ })
+ .catch((error) => {
+ dispatch({ type: ActionTypes.ERROR, error: error?.response?.data });
+ dispatch(stopLoader());
+ throw error;
+ });
+};
+
+export const getCollections = (username, options) =>
+ fetchCollections(username, options, ActionTypes.SET_COLLECTIONS);
+
+export const getCollectionsForCollectionList = (username, options) =>
+ fetchCollections(
+ username,
+ options,
+ ActionTypes.SET_COLLECTIONS_FOR_COLLECTION_LIST
+ );
diff --git a/client/modules/IDE/actions/ide.js b/client/modules/IDE/actions/ide.js
index 80a43443bc..ad16cd9a5d 100644
--- a/client/modules/IDE/actions/ide.js
+++ b/client/modules/IDE/actions/ide.js
@@ -236,23 +236,10 @@ export function hideErrorModal() {
};
}
-export function hideRuntimeErrorWarning() {
- return {
- type: ActionTypes.HIDE_RUNTIME_ERROR_WARNING
- };
-}
-
-export function showRuntimeErrorWarning() {
- return {
- type: ActionTypes.SHOW_RUNTIME_ERROR_WARNING
- };
-}
-
export function startSketch() {
return (dispatch, getState) => {
dispatch(clearConsole());
dispatch(startVisualSketch());
- dispatch(showRuntimeErrorWarning());
const state = getState();
dispatchMessage({
type: MessageTypes.SKETCH,
diff --git a/client/modules/IDE/components/AddToCollectionList.jsx b/client/modules/IDE/components/AddToCollectionList.jsx
index 75713367ee..f3910ab4fc 100644
--- a/client/modules/IDE/components/AddToCollectionList.jsx
+++ b/client/modules/IDE/components/AddToCollectionList.jsx
@@ -4,15 +4,13 @@ import { Helmet } from 'react-helmet';
import { useTranslation } from 'react-i18next';
import { useDispatch, useSelector } from 'react-redux';
import styled from 'styled-components';
+import { addToCollection, removeFromCollection } from '../actions/collections';
+import { getCollectionsForCollectionList } from '../actions/collections';
import { Loader } from '../../App/components/Loader';
-import {
- addToCollection,
- getCollections,
- removeFromCollection
-} from '../actions/collections';
import getSortedCollections from '../selectors/collections';
import QuickAddList from './QuickAddList';
import { remSize } from '../../../theme';
+import Pagination from './Pagination';
export const CollectionAddSketchWrapper = styled.div`
width: ${remSize(600)};
@@ -34,7 +32,19 @@ const AddToCollectionList = ({ projectId }) => {
const username = useSelector((state) => state.user.username);
- const collections = useSelector(getSortedCollections);
+ const collections = useSelector(
+ (state) => state.collectionsListCollections.collections
+ );
+
+ const paginationMeta = useSelector(
+ (state) => state.collectionsListCollections.metadata
+ );
+
+ const q = useSelector((state) => state.search.collectionSearchTerm);
+ const hasCollections = () => collections?.length > 0;
+
+ const [page, setPage] = useState(1);
+ const limit = 10;
// TODO: improve loading state
const loading = useSelector((state) => state.loading);
@@ -42,8 +52,18 @@ const AddToCollectionList = ({ projectId }) => {
const showLoader = loading && !hasLoadedData;
useEffect(() => {
- dispatch(getCollections(username)).then(() => setHasLoadedData(true));
- }, [dispatch, username]);
+ dispatch(
+ getCollectionsForCollectionList(username, {
+ page,
+ limit,
+ q
+ })
+ ).finally(() => setHasLoadedData(true));
+ }, [dispatch, username, page, q]);
+
+ useEffect(() => {
+ setPage(1);
+ }, [q]);
const handleCollectionAdd = (collection) => {
dispatch(addToCollection(collection.id, projectId));
@@ -66,11 +86,23 @@ const AddToCollectionList = ({ projectId }) => {
return t('AddToCollectionList.Empty');
}
return (
-
+ <>
+
+ {hasCollections() && (
+
+ )}
+ >
);
};
diff --git a/client/modules/IDE/components/AddToCollectionSketchList.jsx b/client/modules/IDE/components/AddToCollectionSketchList.jsx
index 29937a6cd0..be4cac59b6 100644
--- a/client/modules/IDE/components/AddToCollectionSketchList.jsx
+++ b/client/modules/IDE/components/AddToCollectionSketchList.jsx
@@ -19,7 +19,6 @@ const AddToCollectionSketchList = ({ collection }) => {
const dispatch = useDispatch();
const username = useSelector((state) => state.user.username);
-
const sketches = useSelector(
(state) => state.collectionsListProjects.projects
);
@@ -34,8 +33,6 @@ const AddToCollectionSketchList = ({ collection }) => {
const [page, setPage] = useState(1);
const limit = 10;
-
- // TODO: improve loading state
const loading = useSelector((state) => state.loading);
const [hasLoadedData, setHasLoadedData] = useState(false);
const showLoader = loading && !hasLoadedData;
diff --git a/client/modules/IDE/components/CollectionList/CollectionList.jsx b/client/modules/IDE/components/CollectionList/CollectionList.jsx
index 9a59c02d8a..dfad473191 100644
--- a/client/modules/IDE/components/CollectionList/CollectionList.jsx
+++ b/client/modules/IDE/components/CollectionList/CollectionList.jsx
@@ -21,13 +21,17 @@ import CollectionListRow from './CollectionListRow';
import ArrowUpIcon from '../../../../images/sort-arrow-up.svg';
import ArrowDownIcon from '../../../../images/sort-arrow-down.svg';
+import Pagination from '../Pagination';
const CollectionList = ({
user,
projectId,
getCollections,
+ getCollectionsForCollectionList,
getProject,
collections,
+ search,
+ paginationMeta,
username: propsUsername,
loading,
toggleDirectionForField,
@@ -36,6 +40,8 @@ const CollectionList = ({
project,
mobile
}) => {
+ const [page, setPage] = useState(1);
+ const limit = mobile ? 7 : 10;
const { t } = useTranslation();
const [hasLoadedData, setHasLoadedData] = useState(false);
const [
@@ -43,13 +49,35 @@ const CollectionList = ({
setAddingSketchesToCollectionId
] = useState(null);
+ const sortField = sorting.field || 'updatedAt';
+ const sortDir =
+ sorting.direction === SortingActions.DIRECTION.ASC ? 'asc' : 'desc';
+
useEffect(() => {
- if (projectId) {
- getProject(projectId);
- }
- getCollections(propsUsername || user.username);
resetSorting();
- }, []);
+ }, [user.username, resetSorting]);
+
+ useEffect(() => {
+ setPage(1);
+ }, [user.username, search, sortField, sortDir]);
+
+ useEffect(() => {
+ getCollectionsForCollectionList(user.username, {
+ page,
+ limit,
+ sortField,
+ sortDir,
+ q: search
+ });
+ }, [
+ getCollectionsForCollectionList,
+ user.username,
+ page,
+ limit,
+ sortField,
+ sortDir,
+ search
+ ]);
useEffect(() => {
if (!loading) {
@@ -74,8 +102,7 @@ const CollectionList = ({
setAddingSketchesToCollectionId(null);
};
- const hasCollections = () =>
- (!loading || hasLoadedData) && collections.length > 0;
+ const hasCollections = () => collections.length > 0;
const renderLoader = () => {
if (loading && !hasLoadedData) return ;
@@ -150,70 +177,83 @@ const CollectionList = ({
};
return (
-
-
- {getTitle}
-
+ <>
+
+
+ {getTitle}
+
- {renderLoader()}
- {renderEmptyTable()}
+ {renderLoader()}
+ {renderEmptyTable()}
+ {hasCollections() && (
+
+
+
+ {renderFieldHeader('name', t('CollectionList.HeaderName'))}
+ {renderFieldHeader(
+ 'createdAt',
+ t('CollectionList.HeaderCreatedAt', {
+ context: mobile ? 'mobile' : ''
+ })
+ )}
+ {renderFieldHeader(
+ 'updatedAt',
+ t('CollectionList.HeaderUpdatedAt', {
+ context: mobile ? 'mobile' : ''
+ })
+ )}
+ {renderFieldHeader(
+ 'numItems',
+ t('CollectionList.HeaderNumItems', {
+ context: mobile ? 'mobile' : ''
+ })
+ )}
+ |
+
+
+
+ {collections.map((collection) => (
+ showAddSketches(collection.id)}
+ />
+ ))}
+
+
+ )}
+ {addingSketchesToCollectionId && (
+ }
+ closeOverlay={hideAddSketches}
+ isFixedHeight
+ >
+
+
+ )}
+
{hasCollections() && (
-
-
-
- {renderFieldHeader('name', t('CollectionList.HeaderName'))}
- {renderFieldHeader(
- 'createdAt',
- t('CollectionList.HeaderCreatedAt', {
- context: mobile ? 'mobile' : ''
- })
- )}
- {renderFieldHeader(
- 'updatedAt',
- t('CollectionList.HeaderUpdatedAt', {
- context: mobile ? 'mobile' : ''
- })
- )}
- {renderFieldHeader(
- 'numItems',
- t('CollectionList.HeaderNumItems', {
- context: mobile ? 'mobile' : ''
- })
- )}
- |
-
-
-
- {collections.map((collection) => (
- showAddSketches(collection.id)}
- />
- ))}
-
-
- )}
- {addingSketchesToCollectionId && (
- }
- closeOverlay={hideAddSketches}
- isFixedHeight
- >
-
-
+
)}
-
+ >
);
};
@@ -222,8 +262,16 @@ CollectionList.propTypes = {
username: PropTypes.string,
authenticated: PropTypes.bool.isRequired
}).isRequired,
+ paginationMeta: PropTypes.shape({
+ page: PropTypes.number.isRequired,
+ totalPages: PropTypes.number.isRequired,
+ totalCollections: PropTypes.number.isRequired,
+ limit: PropTypes.number.isRequired,
+ hasPagination: PropTypes.bool.isRequired
+ }).isRequired,
projectId: PropTypes.string,
getCollections: PropTypes.func.isRequired,
+ getCollectionsForCollectionList: PropTypes.func.isRequired,
getProject: PropTypes.func.isRequired,
collections: PropTypes.arrayOf(
PropTypes.shape({
@@ -242,6 +290,7 @@ CollectionList.propTypes = {
field: PropTypes.string.isRequired,
direction: PropTypes.string.isRequired
}).isRequired,
+ search: PropTypes.string.isRequired,
project: PropTypes.shape({
id: PropTypes.string,
owner: PropTypes.shape({
@@ -264,9 +313,18 @@ CollectionList.defaultProps = {
function mapStateToProps(state, ownProps) {
return {
user: state.user,
- collections: getSortedCollections(state),
+ collections: state.collectionsListCollections.collections ?? [],
+ paginationMeta: state.collectionsListCollections.metadata ?? {
+ page: 1,
+ totalPages: 1,
+ totalCollections: 0,
+ limit: 10,
+ hasPagination: true
+ },
+
sorting: state.sorting,
loading: state.loading,
+ search: state.search.collectionSearchTerm,
project: state.project,
projectId: ownProps && ownProps.params ? ownProps.params.project_id : null
};
diff --git a/client/modules/IDE/components/ConsoleInput.jsx b/client/modules/IDE/components/ConsoleInput.jsx
index 3806db0381..41fc729d4b 100644
--- a/client/modules/IDE/components/ConsoleInput.jsx
+++ b/client/modules/IDE/components/ConsoleInput.jsx
@@ -1,6 +1,16 @@
import PropTypes from 'prop-types';
-import React, { useRef, useEffect, useState } from 'react';
-import CodeMirror from 'codemirror';
+import React, { useRef, useEffect } from 'react';
+import { EditorState } from '@codemirror/state';
+import { EditorView, highlightSpecialChars, keymap } from '@codemirror/view';
+import {
+ bracketMatching,
+ syntaxHighlighting,
+ defaultHighlightStyle
+} from '@codemirror/language';
+import { closeBrackets, closeBracketsKeymap } from '@codemirror/autocomplete';
+import { defaultKeymap, history, historyKeymap } from '@codemirror/commands';
+import { javascript } from '@codemirror/lang-javascript';
+
import { useDispatch } from 'react-redux';
import { Encode } from 'console-feed';
@@ -11,31 +21,24 @@ import { dispatchMessage, MessageTypes } from '../../../utils/dispatcher';
// heavily inspired by
// https://github.com/codesandbox/codesandbox-client/blob/92a1131f4ded6f7d9c16945dc7c18aa97c8ada27/packages/app/src/app/components/Preview/DevTools/Console/Input/index.tsx
+// TODO(connie): Add theme support?
function ConsoleInput({ theme, fontSize }) {
- const [commandHistory, setCommandHistory] = useState([]);
- const [commandCursor, setCommandCursor] = useState(-1);
+ const commandHistory = useRef([]);
+ const commandCursor = useRef(-1);
const codemirrorContainer = useRef(null);
- const cmInstance = useRef(null);
+ const cmView = useRef(null);
const dispatch = useDispatch();
useEffect(() => {
- cmInstance.current = CodeMirror(codemirrorContainer.current, {
- theme: `p5-${theme}`,
- scrollbarStyle: null,
- keymap: 'sublime',
- mode: 'javascript',
- inputStyle: 'contenteditable'
- });
- }, []);
-
- useEffect(() => {
- const handleEnterKey = (cm, e) => {
- if (e.key === 'Enter' && !e.shiftKey) {
- e.preventDefault();
- e.stopPropagation();
-
- const value = cm.getValue().trim();
- if (value === '') return;
+ const enterKeymap = {
+ key: 'Enter',
+ shiftKey: () => false, // Treat like a normal Enter key press if the Shift key is held down.
+ preventDefault: true,
+ stopPropogation: true,
+ run: (view) => {
+ const value = view.state.doc.toString().trim();
+ if (value === '' || view.state.selection.main.empty === false)
+ return false;
const messages = [
{ log: Encode({ method: 'command', data: [value] }) }
@@ -48,77 +51,91 @@ function ConsoleInput({ theme, fontSize }) {
});
dispatch(dispatchConsoleEvent(consoleEvent));
- cm.setValue('');
- setCommandHistory([value, ...commandHistory]);
- setCommandCursor(-1);
- }
- };
-
- if (cmInstance.current) {
- cmInstance.current.on('keydown', handleEnterKey);
- }
-
- return () => {
- if (cmInstance.current) {
- cmInstance.current.off('keydown', handleEnterKey);
+ view.dispatch({
+ changes: { from: 0, to: view.state.doc.length, insert: '' }
+ });
+ commandHistory.current.unshift(value);
+ commandCursor.current = -1;
+ return true;
}
};
- }, [commandHistory]);
- useEffect(() => {
- const handleUpArrowKey = (cm, e) => {
- if (e.key === 'ArrowUp') {
- const lineNumber = cm.getDoc().getCursor().line;
- if (lineNumber !== 0) return;
+ const upArrowKeymap = {
+ key: 'ArrowUp',
+ run: (view) => {
+ // Just let the cursor go up if we have a multiline input
+ // and the cursor isn't at the first line.
+ const currentLine = view.state.doc.lineAt(
+ view.state.selection.main.head
+ ).number;
+ // CM lines are 1-indexed, so the first line is 1.
+ if (currentLine > 1) return false;
const newCursor = Math.min(
- commandCursor + 1,
- commandHistory.length - 1
+ commandCursor.current + 1,
+ commandHistory.current.length - 1
);
- cm.setValue(commandHistory[newCursor] || '');
- const cursorPos = cm.getDoc().getLine(0).length - 1;
- cm.getDoc().setCursor({ line: 0, ch: cursorPos });
- setCommandCursor(newCursor);
- }
- };
-
- if (cmInstance.current) {
- cmInstance.current.on('keydown', handleUpArrowKey);
- }
-
- return () => {
- if (cmInstance.current) {
- cmInstance.current.off('keydown', handleUpArrowKey);
+ const newValue = commandHistory.current[newCursor] || '';
+ view.dispatch({
+ changes: { from: 0, to: view.state.doc.length, insert: newValue }
+ });
+ const newCursorPos = newValue.length;
+ view.dispatch({
+ selection: { anchor: newCursorPos, head: newCursorPos }
+ });
+ commandCursor.current = newCursor;
+ return true;
}
};
- }, [commandCursor, commandHistory]);
- useEffect(() => {
- const handleArrowDownKey = (cm, e) => {
- if (e.key === 'ArrowDown') {
- const lineNumber = cm.getDoc().getCursor().line;
- const lineCount = cm.lineCount();
- if (lineNumber + 1 !== lineCount) return;
-
- const newCursor = Math.max(commandCursor - 1, -1);
- cm.setValue(commandHistory[newCursor] || '');
- const newLine = cm.getDoc().getLine(lineCount - 1);
- const cursorPos = newLine ? newLine.length - 1 : 1;
- cm.getDoc().setCursor({ line: lineCount - 1, ch: cursorPos });
- setCommandCursor(newCursor);
+ const downArrowKeymap = {
+ key: 'ArrowDown',
+ run: (view) => {
+ // Just let the cursor go down if we have a multiline input
+ // and the cursor isn't at the last line.
+ const currentLine = view.state.doc.lineAt(
+ view.state.selection.main.head
+ ).number;
+ const docLength = view.state.doc.lines;
+ if (currentLine !== docLength) return false;
+
+ const newCursor = Math.max(commandCursor.current - 1, -1);
+ const newValue = commandHistory.current[newCursor] || '';
+ view.dispatch({
+ changes: { from: 0, to: view.state.doc.length, insert: newValue }
+ });
+ const newCursorPos = newValue.length;
+ view.dispatch({
+ selection: { anchor: newCursorPos, head: newCursorPos }
+ });
+ commandCursor.current = newCursor;
+ return true;
}
};
- if (cmInstance.current) {
- cmInstance.current.on('keydown', handleArrowDownKey);
- }
-
- return () => {
- if (cmInstance.current) {
- cmInstance.current.off('keydown', handleArrowDownKey);
- }
- };
- }, [commandCursor, commandHistory]);
+ const cmState = EditorState.create({
+ extensions: [
+ history(),
+ highlightSpecialChars(),
+ bracketMatching(),
+ closeBrackets(),
+ syntaxHighlighting(defaultHighlightStyle),
+ javascript(),
+ keymap.of([
+ enterKeymap,
+ upArrowKeymap,
+ downArrowKeymap,
+ ...defaultKeymap,
+ ...closeBracketsKeymap,
+ ...historyKeymap
+ ])
+ ]
+ });
+ cmView.current = new EditorView({
+ state: cmState,
+ parent: codemirrorContainer.current
+ });
+ }, []);
return (
diff --git a/client/modules/IDE/components/Editor/codemirror.js b/client/modules/IDE/components/Editor/codemirror.js
new file mode 100644
index 0000000000..3b122aa6af
--- /dev/null
+++ b/client/modules/IDE/components/Editor/codemirror.js
@@ -0,0 +1,216 @@
+import { useRef, useEffect } from 'react';
+import { EditorView, lineNumbers as lineNumbersExt } from '@codemirror/view';
+import { autocompletion, closeBrackets } from '@codemirror/autocomplete';
+import { debounce } from 'lodash';
+import { openSearchPanel } from '@codemirror/search';
+
+import {
+ getFileMode,
+ createNewFileState,
+ updateFileStates,
+ createAutocompleteOptions
+} from './stateUtils';
+import { useEffectWithComparison } from '../../hooks/custom-hooks';
+import { tidyCodeWithPrettier } from './tidier';
+
+// ----- GENERAL TODOS (in order of priority) -----
+// - revisit keymap differences, esp around sublime
+// - emmet doesn't trigger if text is copy pasted in
+// - need to re-implement emmet auto rename tag
+// - color picker should be triggered by metakey cmd k
+// - clike addon
+
+/** This is a custom React hook that manages CodeMirror state. */
+export default function useCodeMirror({
+ lineNumbers,
+ linewrap,
+ autocloseBracketsQuotes,
+ setUnsavedChanges,
+ setCurrentLine,
+ updateFileContent,
+ file,
+ files,
+ autorefresh,
+ isPlaying,
+ clearConsole,
+ startSketch,
+ autocompleteHinter,
+ fontSize,
+ onUpdateLinting,
+ referenceBaseUrl
+}) {
+ // The codemirror instance.
+ const cmView = useRef();
+ // The current codemirror files.
+ const fileStates = useRef();
+
+ // We have to create a ref for the file ID, or else the debouncer
+ // will old onto an old version of the fileId and just overrwrite the initial file.
+ const fileId = useRef();
+ fileId.current = file.id;
+
+ // When the file changes, update the file content and save status.
+ function onChange() {
+ setUnsavedChanges(true);
+ updateFileContent(fileId.current, cmView.current.state.doc.toString());
+ if (autorefresh && isPlaying) {
+ clearConsole();
+ startSketch();
+ }
+ }
+ // Call onChange at most once every second.
+ const debouncedOnChange = debounce(onChange, 1000);
+
+ // This is called when the CM view updates.
+ function onViewUpdate(updateView) {
+ const { state } = updateView;
+
+ // TODO - check if need to subtract one
+ setCurrentLine(state.doc.lineAt(state.selection.main.head).number);
+
+ if (updateView.docChanged) {
+ debouncedOnChange();
+ }
+ }
+
+ // When the container component enters the DOM, we want this function
+ // to be called so we can setup the CodeMirror instance with the container.
+ function setupCodeMirrorOnContainerMounted(container) {
+ cmView.current = new EditorView({
+ parent: container
+ });
+ }
+
+ // When the component unmounts, we want to clean up the CodeMirror instance.
+ function teardownCodeMirror() {
+ if (cmView.current) {
+ cmView.current.destroy();
+ cmView.current = null;
+ }
+ }
+
+ // When settings change, we pass those changes into CodeMirror.
+ useEffect(() => {
+ cmView.current.dom.style['font-size'] = `${fontSize}px`;
+ }, [fontSize]);
+ useEffect(() => {
+ const reconfigureEffect = (fileState) =>
+ fileState.lineWrappingCpt.reconfigure(
+ linewrap ? EditorView.lineWrapping : []
+ );
+ updateFileStates({
+ fileStates: fileStates.current,
+ cmView: cmView.current,
+ file,
+ reconfigureEffect
+ });
+ }, [linewrap]);
+ useEffect(() => {
+ const reconfigureEffect = (fileState) =>
+ fileState.lineNumbersCpt.reconfigure(lineNumbers ? lineNumbersExt() : []);
+ updateFileStates({
+ fileStates: fileStates.current,
+ cmView: cmView.current,
+ file,
+ reconfigureEffect
+ });
+ }, [lineNumbers]);
+ useEffect(() => {
+ const reconfigureEffect = (fileState) =>
+ fileState.closeBracketsCpt.reconfigure(
+ autocloseBracketsQuotes ? closeBrackets() : []
+ );
+ updateFileStates({
+ fileStates: fileStates.current,
+ cmView: cmView.current,
+ file,
+ reconfigureEffect
+ });
+ }, [autocloseBracketsQuotes]);
+ useEffect(() => {
+ const reconfigureEffect = (fileState) =>
+ fileState.autocompleteCpt.reconfigure(
+ autocompleteHinter
+ ? autocompletion(createAutocompleteOptions(referenceBaseUrl))
+ : []
+ );
+ updateFileStates({
+ fileStates: fileStates.current,
+ cmView: cmView.current,
+ file,
+ reconfigureEffect
+ });
+ }, [autocompleteHinter, referenceBaseUrl]);
+
+ // Initializes the files as CodeMirror states.
+ function initializeDocuments() {
+ if (!fileStates.current) {
+ fileStates.current = {};
+ }
+
+ files.forEach((currentFile) => {
+ if (
+ currentFile.name !== 'root' &&
+ !(currentFile.id in fileStates.current)
+ ) {
+ fileStates.current[currentFile.id] = createNewFileState(
+ currentFile.name,
+ currentFile.content,
+ {
+ linewrap,
+ lineNumbers,
+ autocloseBracketsQuotes,
+ autocomplete: autocompleteHinter,
+ onUpdateLinting,
+ onViewUpdate,
+ referenceBaseUrl
+ }
+ );
+ }
+ });
+ }
+
+ // When the files change, reinitialize the documents.
+ useEffect(initializeDocuments, [files]);
+
+ // When the file changes, make the CodeMirror call to swap out the document.
+ useEffectWithComparison(
+ (_, prevProps) => {
+ // We need to save the previous CodeMirror state so we can restore it
+ // when we switch back to it.
+ const previousState = cmView.current.state;
+ if (Array.isArray(prevProps) && prevProps.length > 0 && previousState) {
+ const prevId = prevProps[0];
+ fileStates.current[prevId].cmState = previousState;
+ }
+
+ const { cmState } = fileStates.current[file.id];
+ cmView.current.setState(cmState);
+ },
+ [file.id]
+ );
+
+ const getContent = () => {
+ const content = cmView.current.state.doc.toString();
+ const updatedFile = Object.assign({}, file, { content });
+ return updatedFile;
+ };
+
+ const showSearch = () => {
+ openSearchPanel(cmView.current);
+ };
+
+ const tidyCode = () => {
+ const fileMode = getFileMode(file.name);
+ tidyCodeWithPrettier(cmView.current, fileMode);
+ };
+
+ return {
+ setupCodeMirrorOnContainerMounted,
+ teardownCodeMirror,
+ getContent,
+ tidyCode,
+ showSearch,
+ codemirrorView: cmView
+ };
+}
diff --git a/client/modules/IDE/components/Editor/consoleErrorDecoration.js b/client/modules/IDE/components/Editor/consoleErrorDecoration.js
new file mode 100644
index 0000000000..ce3890bcca
--- /dev/null
+++ b/client/modules/IDE/components/Editor/consoleErrorDecoration.js
@@ -0,0 +1,52 @@
+import { StateField, StateEffect } from '@codemirror/state';
+import { EditorView, Decoration } from '@codemirror/view';
+
+// Effects for communicating with the state field
+const ADD_ERROR_DECORATION = StateEffect.define();
+const FILTER_ERROR_DECORATION = StateEffect.define();
+
+// An extension for managing error line decorations
+// Mostly adapted from the Marked Text demo in https://codemirror.net/docs/migration/
+// You can affect this by calling addErrorDecoration and removeErrorDecorations
+export const errorDecorationStateField = StateField.define({
+ // Start with an empty set of decorations
+ create() {
+ return Decoration.none;
+ },
+ // This is called whenever the editor updates
+ update(value, transaction) {
+ // Move the decorations to account for document changes
+ let newValue = value.map(transaction.changes);
+ for (let i = 0; i < transaction.effects.length; i++) {
+ const effect = transaction.effects[i];
+ if (effect.is(ADD_ERROR_DECORATION))
+ newValue = newValue.update({ add: effect.value, sort: true });
+ else if (effect.is(FILTER_ERROR_DECORATION))
+ newValue = newValue.update({ filter: effect.value });
+ }
+ return newValue;
+ },
+ // Indicate that this field provides a set of decorations
+ provide: (f) => EditorView.decorations.from(f)
+});
+
+const ERROR_DECORATION = Decoration.line({
+ class: 'cm-errorLine' // Defined in _editor.scss
+});
+
+// Add an error decoration to a specific line number
+export function addErrorDecoration(view, lineNumber) {
+ const docLineNumber = view.state.doc.line(lineNumber);
+ view.dispatch({
+ effects: ADD_ERROR_DECORATION.of([
+ ERROR_DECORATION.range(docLineNumber.from)
+ ])
+ });
+}
+
+// Remove all error decorations
+export function removeErrorDecorations(view) {
+ view.dispatch({
+ effects: FILTER_ERROR_DECORATION.of(() => false)
+ });
+}
diff --git a/client/modules/IDE/components/Editor/highlightStyle.js b/client/modules/IDE/components/Editor/highlightStyle.js
new file mode 100644
index 0000000000..18b3d53c21
--- /dev/null
+++ b/client/modules/IDE/components/Editor/highlightStyle.js
@@ -0,0 +1,58 @@
+import { HighlightStyle } from '@codemirror/language';
+import { tags } from '@lezer/highlight';
+
+export const highlightStyle = HighlightStyle.define([
+ { tag: tags.comment, class: 'cm-comment' },
+ { tag: tags.lineComment, class: 'cm-comment' },
+ { tag: tags.blockComment, class: 'cm-comment' },
+ { tag: tags.docComment, class: 'cm-comment' },
+ { tag: tags.docString, class: 'cm-comment' },
+ { tag: tags.name, class: 'cm-variable' },
+ { tag: tags.variableName, class: 'cm-variable' },
+ { tag: tags.typeName, class: 'cm-variable' },
+ { tag: tags.className, class: 'cm-variable' },
+ { tag: tags.string, class: 'cm-string' },
+ { tag: tags.character, class: 'cm-string' },
+ { tag: tags.attributeName, class: 'cm-string' },
+ { tag: tags.regexp, class: 'cm-regexp' },
+ { tag: tags.number, class: 'cm-number' },
+ { tag: tags.integer, class: 'cm-number' },
+ { tag: tags.float, class: 'cm-number' },
+ { tag: tags.atom, class: 'cm-atom' },
+ { tag: tags.bool, class: 'cm-atom' },
+ { tag: tags.null, class: 'cm-atom' },
+ { tag: tags.keyword, class: 'cm-keyword' },
+ { tag: tags.self, class: 'cm-keyword' },
+ { tag: tags.function, class: 'cm-keyword' },
+ { tag: tags.operator, class: 'cm-operator' },
+ { tag: tags.operatorKeyword, class: 'cm-operator' },
+ { tag: tags.controlKeyword, class: 'cm-operator' },
+ { tag: tags.derefOperator, class: 'cm-operator' },
+ { tag: tags.arithmeticOperator, class: 'cm-operator' },
+ { tag: tags.logicOperator, class: 'cm-operator' },
+ { tag: tags.bitwiseOperator, class: 'cm-operator' },
+ { tag: tags.compareOperator, class: 'cm-operator' },
+ { tag: tags.updateOperator, class: 'cm-operator' },
+ { tag: tags.typeOperator, class: 'cm-operator' },
+ { tag: tags.controlOperator, class: 'cm-operator' },
+ { tag: tags.definitionKeyword, class: 'cm-keyword' },
+ { tag: tags.tagName, class: 'cm-tag' },
+ { tag: tags.heading, class: 'cm-tag' },
+ { tag: tags.heading1, class: 'cm-tag' },
+ { tag: tags.heading2, class: 'cm-tag' },
+ { tag: tags.heading3, class: 'cm-tag' },
+ { tag: tags.heading4, class: 'cm-tag' },
+ { tag: tags.heading5, class: 'cm-tag' },
+ { tag: tags.heading6, class: 'cm-tag' },
+ { tag: tags.list, class: 'cm-tag' },
+ { tag: tags.quote, class: 'cm-tag' },
+ { tag: tags.emphasis, class: 'cm-tag' },
+ { tag: tags.strong, class: 'cm-tag' },
+ { tag: tags.link, class: 'cm-tag' },
+ { tag: tags.propertyName, class: 'cm-property' },
+ { tag: tags.attributeName, class: 'cm-attribute' }
+
+ // TODO(connie): Add p5 specific highlighting, like .p5-variable
+ // There might be a way to do this with Lezer's special tags like
+ // special(), definition(), const(), local()
+]);
diff --git a/client/modules/IDE/components/Editor/index.jsx b/client/modules/IDE/components/Editor/index.jsx
index 474808da1c..23f1c69900 100644
--- a/client/modules/IDE/components/Editor/index.jsx
+++ b/client/modules/IDE/components/Editor/index.jsx
@@ -1,55 +1,13 @@
-// TODO: convert to functional component
-
import PropTypes from 'prop-types';
-import React from 'react';
-import CodeMirror from 'codemirror';
-import Fuse from 'fuse.js';
-import emmet from '@emmetio/codemirror-plugin';
-import prettier from 'prettier/standalone';
-import babelParser from 'prettier/parser-babel';
-import htmlParser from 'prettier/parser-html';
-import cssParser from 'prettier/parser-postcss';
+import React, { useCallback, useEffect, useRef, useState } from 'react';
import { withTranslation } from 'react-i18next';
import StackTrace from 'stacktrace-js';
-import 'codemirror/mode/css/css';
-import 'codemirror/mode/clike/clike';
-import 'codemirror/addon/selection/active-line';
-import 'codemirror/addon/lint/lint';
-import 'codemirror/addon/lint/javascript-lint';
-import 'codemirror/addon/lint/css-lint';
-import 'codemirror/addon/lint/html-lint';
-import 'codemirror/addon/fold/brace-fold';
-import 'codemirror/addon/fold/comment-fold';
-import 'codemirror/addon/fold/foldcode';
-import 'codemirror/addon/fold/foldgutter';
-import 'codemirror/addon/fold/indent-fold';
-import 'codemirror/addon/fold/xml-fold';
-import 'codemirror/addon/comment/comment';
-import 'codemirror/keymap/sublime';
-import 'codemirror/addon/search/searchcursor';
-import 'codemirror/addon/search/matchesonscrollbar';
-import 'codemirror/addon/search/match-highlighter';
-import 'codemirror/addon/search/jump-to-line';
-import 'codemirror/addon/edit/matchbrackets';
-import 'codemirror/addon/edit/closebrackets';
-import 'codemirror/addon/selection/mark-selection';
-import 'codemirror/addon/hint/css-hint';
-import 'codemirror-colorpicker';
-import { JSHINT } from 'jshint';
-import { CSSLint } from 'csslint';
-import { HTMLHint } from 'htmlhint';
import classNames from 'classnames';
import { debounce } from 'lodash';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import MediaQuery from 'react-responsive';
-import '../../../../utils/htmlmixed';
-import '../../../../utils/p5-javascript';
-import { metaKey } from '../../../../utils/metaKey';
-import '../show-hint';
-import * as hinter from '../../../../utils/p5-hinter';
-import '../../../../utils/codemirror-search';
import beepUrl from '../../../../sounds/audioAlert.mp3';
import RightArrowIcon from '../../../../images/right-arrow.svg';
@@ -73,629 +31,218 @@ import { EditorContainer, EditorHolder } from './MobileEditor';
import { FolderIcon } from '../../../../common/icons';
import { IconButton } from '../../../../common/IconButton';
-import contextAwareHinter from '../../../../utils/contextAwareHinter';
-import showRenameDialog from '../../../../utils/showRenameDialog';
-import handleRename from '../../../../utils/rename-variable';
-import { jumpToDefinition } from '../../../../utils/jump-to-definition';
-import { ensureAriaLiveRegion } from '../../../../utils/ScreenReaderHelper';
-import { isMac } from '../../../../utils/device';
-
-emmet(CodeMirror);
-
-window.JSHINT = JSHINT;
-window.CSSLint = CSSLint;
-window.HTMLHint = HTMLHint;
-
-const INDENTATION_AMOUNT = 2;
-
-class Editor extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- currentLine: 1
- };
- this._cm = null;
- this.tidyCode = this.tidyCode.bind(this);
-
- this.updateLintingMessageAccessibility = debounce((annotations) => {
- this.props.clearLintMessage();
- annotations.forEach((x) => {
- if (x.from.line > -1) {
- this.props.updateLintMessage(x.severity, x.from.line + 1, x.message);
- }
- });
- if (this.props.lintMessages.length > 0 && this.props.lintWarning) {
- this.beep.play();
- }
- }, 2000);
- this.showFind = this.showFind.bind(this);
- this.showReplace = this.showReplace.bind(this);
- this.getContent = this.getContent.bind(this);
- this.updateFileContent = this.updateFileContent.bind(this);
- }
-
- componentDidMount() {
- this.beep = new Audio(beepUrl);
- ensureAriaLiveRegion();
- // this.widgets = [];
- this._cm = CodeMirror(this.codemirrorContainer, {
- theme: `p5-${this.props.theme}`,
- lineNumbers: this.props.lineNumbers,
- styleActiveLine: true,
- inputStyle: 'contenteditable',
- lineWrapping: this.props.linewrap,
- fixedGutter: false,
- foldGutter: true,
- foldOptions: { widget: '\u2026' },
- gutters: ['CodeMirror-foldgutter', 'CodeMirror-lint-markers'],
- keyMap: 'sublime',
- highlightSelectionMatches: true, // highlight current search match
- matchBrackets: true,
- emmet: {
- preview: ['html'],
- markTagPairs: true,
- autoRenameTags: true
- },
- autoCloseBrackets: this.props.autocloseBracketsQuotes,
- styleSelectedText: true,
- lint: {
- onUpdateLinting: (annotations) => {
- this.updateLintingMessageAccessibility(annotations);
- },
- options: {
- asi: true,
- eqeqeq: false,
- '-W041': false,
- esversion: 11
- }
- },
- colorpicker: {
- type: 'sketch',
- mode: 'edit'
- }
- });
-
- this.hinter = new Fuse(hinter.p5Hinter, {
- threshold: 0.05,
- keys: ['text']
- });
-
- delete this._cm.options.lint.options.errors;
-
- this._cm.getWrapperElement().addEventListener('click', (e) => {
- const isCtrlClick = isMac() ? e.metaKey : e.ctrlKey;
+import useCodeMirror from './codemirror';
- if (isCtrlClick) {
- const pos = this._cm.coordsChar({ left: e.clientX, top: e.clientY });
- jumpToDefinition.call(this, pos);
- }
- });
-
- const renameKey = isMac() ? 'Ctrl-F2' : 'F2';
-
- const replaceCommand =
- metaKey === 'Ctrl' ? `${metaKey}-H` : `${metaKey}-Option-F`;
- this._cm.setOption('extraKeys', {
- Tab: (cm) => {
- if (!cm.execCommand('emmetExpandAbbreviation')) return;
- // might need to specify and indent more?
- const selection = cm.doc.getSelection();
- if (selection.length > 0) {
- cm.execCommand('indentMore');
- } else {
- cm.replaceSelection(' '.repeat(INDENTATION_AMOUNT));
- }
- },
- Enter: 'emmetInsertLineBreak',
- Esc: 'emmetResetAbbreviation',
- [`Shift-${metaKey}-E`]: (cm) => {
- cm.getInputField().blur();
- },
- [renameKey]: (cm) => this.renameVariable(cm),
- [`Shift-Tab`]: false,
- [`${metaKey}-Enter`]: () => null,
- [`Shift-${metaKey}-Enter`]: () => null,
- [`${metaKey}-F`]: 'findPersistent',
- [`Shift-${metaKey}-F`]: this.tidyCode,
- [`${metaKey}-G`]: 'findPersistentNext',
- [`Shift-${metaKey}-G`]: 'findPersistentPrev',
- [replaceCommand]: 'replace',
- // Cassie Tarakajian: If you don't set a default color, then when you
- // choose a color, it deletes characters inline. This is a
- // hack to prevent that.
- [`${metaKey}-K`]: (cm, event) =>
- cm.state.colorpicker.popup_color_picker({ length: 0 }),
- [`${metaKey}-.`]: 'toggleComment' // Note: most adblockers use the shortcut ctrl+.
- });
+import {
+ addErrorDecoration,
+ removeErrorDecorations
+} from './consoleErrorDecoration';
- this.initializeDocuments(this.props.files);
- this._cm.swapDoc(this._docs[this.props.file.id]);
+// temporary until p5.js 2.0 becomes default
+// checks if sketch is using p5.js 2.0 to pass correct base url for autocomplete hinter reference
+export function getReferenceBaseUrl(htmlFile) {
+ const html = htmlFile?.content || '';
- this._cm.on(
- 'change',
- debounce(() => {
- this.props.setUnsavedChanges(true);
- this.props.hideRuntimeErrorWarning();
- this.props.updateFileContent(this.props.file.id, this._cm.getValue());
- if (this.props.autorefresh && this.props.isPlaying) {
- this.props.clearConsole();
- this.props.startSketch();
- }
- }, 1000)
- );
+ const isV2 =
+ /https:\/\/beta\.p5js\.org\b/i.test(html) || /\bp5(@|-)2\./i.test(html);
- if (this._cm) {
- this._cm.on('keyup', this.handleKeyUp);
- }
+ return isV2 ? 'https://beta.p5js.org' : 'https://p5js.org';
+}
- this._cm.on('keydown', (_cm, e) => {
- // Skip hinting if the user is pasting (Ctrl/Cmd+V) or using modifier keys (Ctrl/Alt)
- if (
- ((e.ctrlKey || e.metaKey) && e.key === 'v') ||
- e.ctrlKey ||
- e.altKey
- ) {
- return;
- }
- const mode = this._cm.getOption('mode');
- if (/^[a-z]$/i.test(e.key) && (mode === 'css' || mode === 'javascript')) {
- this.showHint(_cm);
+function Editor({
+ provideController,
+ files,
+ file,
+ linewrap,
+ lineNumbers,
+ closeProjectOptions,
+ setSelectedFile,
+ setUnsavedChanges,
+ lintMessages,
+ lintWarning,
+ clearLintMessage,
+ updateLintMessage,
+ updateFileContent,
+ autorefresh,
+ isPlaying,
+ clearConsole,
+ startSketch,
+ autocompleteHinter,
+ autocloseBracketsQuotes,
+ fontSize,
+ consoleEvents,
+ expandConsole,
+ isExpanded,
+ htmlFile,
+ t,
+ collapseSidebar,
+ expandSidebar
+}) {
+ const [currentLine, setCurrentLine] = useState(1);
+ const beep = useRef();
+
+ const updateLintingMessageAccessibility = debounce((annotations) => {
+ clearLintMessage();
+ annotations.forEach((x) => {
+ if (x.from.line > -1) {
+ updateLintMessage(x.severity, x.from.line + 1, x.message);
}
});
-
- this._cm.getWrapperElement().style[
- 'font-size'
- ] = `${this.props.fontSize}px`;
-
- this.props.provideController({
- tidyCode: this.tidyCode,
- showFind: this.showFind,
- showReplace: this.showReplace,
- getContent: this.getContent,
- updateFileContent: this.updateFileContent
- });
- }
-
- componentWillUpdate(nextProps) {
- // check if files have changed
- if (this.props.files[0].id !== nextProps.files[0].id) {
- // then need to make CodeMirror documents
- this.initializeDocuments(nextProps.files);
+ if (lintMessages.length > 0 && lintWarning) {
+ beep.play();
}
- if (this.props.files.length !== nextProps.files.length) {
- this.initializeDocuments(nextProps.files);
- }
- }
-
- componentDidUpdate(prevProps) {
- if (this.props.file.id !== prevProps.file.id) {
- const fileMode = this.getFileMode(this.props.file.name);
- if (fileMode === 'javascript') {
- // Define the new Emmet configuration based on the file mode
- const emmetConfig = {
- preview: ['html'],
- markTagPairs: false,
- autoRenameTags: true
- };
- this._cm.setOption('emmet', emmetConfig);
- }
- const oldDoc = this._cm.swapDoc(this._docs[this.props.file.id]);
- this._docs[prevProps.file.id] = oldDoc;
- this._cm.focus();
-
- if (!prevProps.unsavedChanges) {
- setTimeout(() => this.props.setUnsavedChanges(false), 400);
- }
- } else if (this.getContent().content !== this.props.file.content) {
- // TODO: make this not break regular edits!
- // this._cm.setValue(this.props.file.content);
- }
- if (this.props.fontSize !== prevProps.fontSize) {
- this._cm.getWrapperElement().style[
- 'font-size'
- ] = `${this.props.fontSize}px`;
- }
- if (this.props.linewrap !== prevProps.linewrap) {
- this._cm.setOption('lineWrapping', this.props.linewrap);
- }
- if (this.props.theme !== prevProps.theme) {
- this._cm.setOption('theme', `p5-${this.props.theme}`);
- }
- if (this.props.lineNumbers !== prevProps.lineNumbers) {
- this._cm.setOption('lineNumbers', this.props.lineNumbers);
- }
- if (
- this.props.autocloseBracketsQuotes !== prevProps.autocloseBracketsQuotes
- ) {
- this._cm.setOption(
- 'autoCloseBrackets',
- this.props.autocloseBracketsQuotes
- );
- }
- if (this.props.autocompleteHinter !== prevProps.autocompleteHinter) {
- if (!this.props.autocompleteHinter) {
- // close the hinter window once the preference is turned off
- CodeMirror.showHint(this._cm, () => {}, {});
- }
- }
-
- if (this.props.runtimeErrorWarningVisible) {
- if (this.props.consoleEvents.length !== prevProps.consoleEvents.length) {
- this.props.consoleEvents.forEach((consoleEvent) => {
- if (consoleEvent.method === 'error') {
- // It doesn't work if you create a new Error, but this works
- // LOL
- const errorObj = { stack: consoleEvent.data[0].toString() };
- StackTrace.fromError(errorObj).then((stackLines) => {
- this.props.expandConsole();
- const line = stackLines.find(
- (l) => l.fileName && l.fileName.startsWith('/')
- );
- if (!line) return;
- const fileNameArray = line.fileName.split('/');
- const fileName = fileNameArray.slice(-1)[0];
- const filePath = fileNameArray.slice(0, -1).join('/');
- const fileWithError = this.props.files.find(
- (f) => f.name === fileName && f.filePath === filePath
- );
- this.props.setSelectedFile(fileWithError.id);
- this._cm.addLineClass(
- line.lineNumber - 1,
- 'background',
- 'line-runtime-error'
- );
- });
- }
- });
- } else {
- for (let i = 0; i < this._cm.lineCount(); i += 1) {
- this._cm.removeLineClass(i, 'background', 'line-runtime-error');
- }
- }
- }
-
- if (this.props.file.id !== prevProps.file.id) {
- for (let i = 0; i < this._cm.lineCount(); i += 1) {
- this._cm.removeLineClass(i, 'background', 'line-runtime-error');
- }
- }
-
- this.props.provideController({
- tidyCode: this.tidyCode,
- showFind: this.showFind,
- showReplace: this.showReplace,
- getContent: this.getContent,
- updateFileContent: this.updateFileContent
+ }, 2000);
+
+ // The useCodeMirror hook manages CodeMirror state and returns
+ // a reference to the actual CM instance.
+ const {
+ setupCodeMirrorOnContainerMounted,
+ teardownCodeMirror,
+ codemirrorView,
+ getContent,
+ tidyCode,
+ showSearch
+ } = useCodeMirror({
+ lineNumbers,
+ linewrap,
+ autocloseBracketsQuotes,
+ setUnsavedChanges,
+ updateFileContent,
+ file,
+ files,
+ autorefresh,
+ isPlaying,
+ clearConsole,
+ startSketch,
+ autocompleteHinter,
+ fontSize,
+ updateLintingMessageAccessibility,
+ setCurrentLine,
+ referenceBaseUrl: getReferenceBaseUrl(htmlFile)
+ });
+
+ // Lets the parent component access file content-specific functionality...
+ useEffect(() => {
+ provideController({
+ tidyCode,
+ getContent,
+ showSearch
});
- }
-
- componentWillUnmount() {
- if (this._cm) {
- this._cm.off('keyup', this.handleKeyUp);
- }
- this.props.provideController(null);
- }
+ }, [getContent]);
- getFileMode(fileName) {
- let mode;
- if (fileName.match(/.+\.js$/i)) {
- mode = 'javascript';
- } else if (fileName.match(/.+\.css$/i)) {
- mode = 'css';
- } else if (fileName.match(/.+\.(html|xml)$/i)) {
- mode = 'htmlmixed';
- } else if (fileName.match(/.+\.json$/i)) {
- mode = 'application/json';
- } else if (fileName.match(/.+\.(frag|glsl)$/i)) {
- mode = 'x-shader/x-fragment';
- } else if (fileName.match(/.+\.(vert|stl|mtl)$/i)) {
- mode = 'x-shader/x-vertex';
- } else {
- mode = 'text/plain';
- }
- return mode;
- }
+ // When the CM container div mounts, we set up CodeMirror.
+ const onContainerMounted = useCallback(setupCodeMirrorOnContainerMounted, []);
- getContent() {
- const content = this._cm.getValue();
- const updatedFile = Object.assign({}, this.props.file, { content });
- return updatedFile;
- }
+ // This is acting as a "componentDidMount" call where it runs once
+ // at the start and never again. It also provides a cleanup function.
+ useEffect(() => {
+ beep.current = new Audio(beepUrl);
- updateFileContent(id, src) {
- const file = this._docs[id];
- if (file) {
- this._docs[id] = CodeMirror.Doc(src, this._docs[id].modeOption);
- if (id === this.props.file.id) {
- this._cm.swapDoc(this._docs[id]);
- }
- }
- }
-
- handleKeyUp = () => {
- const lineNumber = parseInt(this._cm.getCursor().line + 1, 10);
- this.setState({ currentLine: lineNumber });
- };
-
- showFind() {
- this._cm.execCommand('findPersistent');
- }
-
- // temporary until p5.js 2.0 becomes default
- // checks if sketch is using p5.js 2.0 to pass correct base url for autocomplete hinter reference
- getReferenceBaseUrl = () => {
- const html = this.props.htmlFile?.content || '';
-
- const isV2 =
- /https:\/\/beta\.p5js\.org\b/i.test(html) || /\bp5(@|-)2\./i.test(html);
-
- return isV2 ? 'https://beta.p5js.org' : 'https://p5js.org';
- };
-
- showHint(_cm) {
- if (!_cm) return;
-
- if (!this.props.autocompleteHinter) {
- CodeMirror.showHint(_cm, () => {}, {});
- return;
- }
-
- let focusedLinkElement = null;
-
- const setFocusedLinkElement = (set) => {
- if (set && !focusedLinkElement) {
- const activeItemLink = document.querySelector(
- `.CodeMirror-hint-active a`
- );
- if (activeItemLink) {
- focusedLinkElement = activeItemLink;
- focusedLinkElement.classList.add('focused-hint-link');
- focusedLinkElement.parentElement.parentElement.classList.add(
- 'unfocused'
- );
- }
- }
+ return () => {
+ provideController(null);
+ teardownCodeMirror();
};
-
- const removeFocusedLinkElement = () => {
- if (focusedLinkElement) {
- focusedLinkElement.classList.remove('focused-hint-link');
- focusedLinkElement.parentElement.parentElement.classList.remove(
- 'unfocused'
+ }, []);
+
+ // Updates the runtime error console.
+ useEffect(() => {
+ const consoleErrors = consoleEvents.filter((e) => e.method === 'error');
+
+ if (consoleErrors.length > 0) {
+ const firstError = consoleErrors[0];
+ const errorObj = { stack: firstError.data[0].toString() };
+ StackTrace.fromError(errorObj).then((stackLines) => {
+ expandConsole();
+ const line = stackLines.find(
+ (l) => l.fileName && l.fileName.startsWith('/')
);
- focusedLinkElement = null;
- return true;
- }
- return false;
- };
-
- const hintOptions = {
- _fontSize: this.props.fontSize,
- referenceBaseUrl: this.getReferenceBaseUrl(),
- completeSingle: false,
- extraKeys: {
- 'Shift-Right': (cm, e) => {
- const activeItemLink = document.querySelector(
- `.CodeMirror-hint-active a`
- );
- if (activeItemLink) activeItemLink.click();
- },
- Right: (cm, e) => setFocusedLinkElement(true),
- Left: (cm, e) => removeFocusedLinkElement(),
- Up: (cm, e) => {
- const onLink = removeFocusedLinkElement();
- e.moveFocus(-1);
- setFocusedLinkElement(onLink);
- },
- Down: (cm, e) => {
- const onLink = removeFocusedLinkElement();
- e.moveFocus(1);
- setFocusedLinkElement(onLink);
- },
- Enter: (cm, e) => {
- if (focusedLinkElement) focusedLinkElement.click();
- else e.pick();
- }
- },
- closeOnUnfocus: false
- };
-
- const triggerHints = () => {
- if (_cm.options.mode === 'javascript') {
- CodeMirror.showHint(
- _cm,
- () => {
- const c = _cm.getCursor();
- const token = _cm.getTokenAt(c);
- const hints = contextAwareHinter(_cm, { hinter: this.hinter });
- return {
- list: hints,
- from: CodeMirror.Pos(c.line, token.start),
- to: CodeMirror.Pos(c.line, c.ch)
- };
- },
- hintOptions
+ if (!line) return;
+ const fileNameArray = line.fileName.split('/');
+ const fileName = fileNameArray.slice(-1)[0];
+ const filePath = fileNameArray.slice(0, -1).join('/');
+ const fileWithError = files.find(
+ (f) => f.name === fileName && f.filePath === filePath
);
- } else if (_cm.options.mode === 'css') {
- CodeMirror.showHint(_cm, CodeMirror.hint.css, hintOptions);
- }
- };
-
- setTimeout(triggerHints, 0);
- }
-
- showReplace() {
- this._cm.execCommand('replace');
- }
-
- prettierFormatWithCursor(parser, plugins) {
- try {
- const { formatted, cursorOffset } = prettier.formatWithCursor(
- this._cm.doc.getValue(),
- {
- cursorOffset: this._cm.doc.indexFromPos(this._cm.doc.getCursor()),
- parser,
- plugins
- }
- );
- const { left, top } = this._cm.getScrollInfo();
- this._cm.doc.setValue(formatted);
- this._cm.focus();
- this._cm.doc.setCursor(this._cm.doc.posFromIndex(cursorOffset));
- this._cm.scrollTo(left, top);
- } catch (error) {
- console.error(error);
- }
- }
-
- tidyCode() {
- const mode = this._cm.getOption('mode');
- if (mode === 'javascript') {
- this.prettierFormatWithCursor('babel', [babelParser]);
- } else if (mode === 'css') {
- this.prettierFormatWithCursor('css', [cssParser]);
- } else if (mode === 'htmlmixed') {
- this.prettierFormatWithCursor('html', [htmlParser]);
- }
- }
-
- renameVariable(cm) {
- const cursorCoords = cm.cursorCoords(true, 'page');
- const selection = cm.getSelection();
- const pos = cm.getCursor(); // or selection start
- const token = cm.getTokenAt(pos);
- const tokenType = token.type;
- if (!selection) {
- return;
+ setSelectedFile(fileWithError.id);
+ addErrorDecoration(codemirrorView.current, line.lineNumber);
+ });
+ } else {
+ removeErrorDecorations(codemirrorView.current);
}
-
- const sel = cm.listSelections()[0];
- const fromPos =
- CodeMirror.cmpPos(sel.anchor, sel.head) <= 0 ? sel.anchor : sel.head;
-
- showRenameDialog(
- cm,
- fromPos,
- tokenType,
- cursorCoords,
- selection,
- (newName) => {
- if (newName && newName.trim() !== '' && newName !== selection) {
- handleRename(fromPos, selection, newName, cm);
- }
- }
- );
- }
-
- initializeDocuments(files) {
- this._docs = {};
- files.forEach((file) => {
- if (file.name !== 'root') {
- this._docs[file.id] = CodeMirror.Doc(
- file.content,
- this.getFileMode(file.name)
- ); // eslint-disable-line
- }
- });
- }
-
- render() {
- const editorSectionClass = classNames({
- editor: true,
- 'sidebar--contracted': !this.props.isExpanded
- });
-
- const editorHolderClass = classNames({
- 'editor-holder': true,
- 'editor-holder--hidden':
- this.props.file.fileType === 'folder' || this.props.file.url
- });
-
- const { currentLine } = this.state;
-
- return (
-
- {(matches) =>
- matches ? (
-
-
-
-
-
-
- {this.props.file.name}
-
-
-
-
-
- {
- this.codemirrorContainer = element;
+ }, [consoleEvents]);
+
+ const editorSectionClass = classNames({
+ editor: true,
+ 'sidebar--contracted': !isExpanded
+ });
+
+ const editorHolderClass = classNames({
+ 'editor-holder': true,
+ 'editor-holder--hidden': file.fileType === 'folder' || file.url
+ });
+
+ return (
+
+ {(matches) =>
+ matches ? (
+
+
+
+
+
+
+ {file.name}
+
+
+
+
+
+
+ {file.url ? : null}
+
+
+ ) : (
+
+
+
+
+ {file.name}
+
+
+
+
+
+ {file.url ? (
+
) : null}
- ) : (
-
-
-
-
- {this.props.file.name}
-
-
-
-
- {
- this.codemirrorContainer = element;
- }}
- />
- {this.props.file.url ? (
-
- ) : null}
-
-
-
- )
- }
-
- );
- }
+
+ )
+ }
+
+ );
}
Editor.propTypes = {
@@ -734,8 +281,6 @@ Editor.propTypes = {
startSketch: PropTypes.func.isRequired,
autorefresh: PropTypes.bool.isRequired,
isPlaying: PropTypes.bool.isRequired,
- theme: PropTypes.string.isRequired,
- unsavedChanges: PropTypes.bool.isRequired,
files: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string.isRequired,
@@ -743,16 +288,14 @@ Editor.propTypes = {
content: PropTypes.string.isRequired
})
).isRequired,
+ isExpanded: PropTypes.bool.isRequired,
htmlFile: PropTypes.shape({
content: PropTypes.string
}),
- isExpanded: PropTypes.bool.isRequired,
collapseSidebar: PropTypes.func.isRequired,
closeProjectOptions: PropTypes.func.isRequired,
expandSidebar: PropTypes.func.isRequired,
clearConsole: PropTypes.func.isRequired,
- hideRuntimeErrorWarning: PropTypes.func.isRequired,
- runtimeErrorWarningVisible: PropTypes.bool.isRequired,
provideController: PropTypes.func.isRequired,
t: PropTypes.func.isRequired,
setSelectedFile: PropTypes.func.isRequired,
diff --git a/client/modules/IDE/components/Editor/p5CompletionPreview.js b/client/modules/IDE/components/Editor/p5CompletionPreview.js
new file mode 100644
index 0000000000..5538744db7
--- /dev/null
+++ b/client/modules/IDE/components/Editor/p5CompletionPreview.js
@@ -0,0 +1,108 @@
+import { StateField, RangeSetBuilder } from '@codemirror/state';
+import { Decoration, EditorView, WidgetType } from '@codemirror/view';
+import { selectedCompletion, completionStatus } from '@codemirror/autocomplete';
+
+class GhostTextWidget extends WidgetType {
+ constructor(text) {
+ super();
+ this.text = text;
+ }
+
+ eq(other) {
+ return other.text === this.text;
+ }
+
+ toDOM() {
+ const span = document.createElement('span');
+ span.className = 'cm-ghostCompletion';
+ span.textContent = this.text;
+ return span;
+ }
+
+ ignoreEvent() {
+ return true;
+ }
+}
+
+function getCurrentWord(state) {
+ const { from, to, empty } = state.selection.main;
+ if (!empty) return null;
+
+ const line = state.doc.lineAt(from);
+ const before = line.text.slice(0, from - line.from);
+ const match = before.match(/\w+$/);
+
+ if (!match) return null;
+
+ const word = match[0];
+ return {
+ text: word,
+ from: from - word.length,
+ to
+ };
+}
+
+function buildGhostText(state) {
+ // only show ghost text if autocomplete is on,
+ // user is typing, and if preview matches typed text
+
+ if (completionStatus(state) !== 'active') return null;
+
+ const selected = selectedCompletion(state);
+ if (!selected) return null;
+
+ const word = getCurrentWord(state);
+ if (!word) return null;
+
+ const preview = selected.preview || selected.label;
+ if (!preview) return null;
+
+ if (!preview.toLowerCase().startsWith(word.text.toLowerCase())) return null;
+
+ const remainder = preview.slice(word.text.length);
+ if (!remainder) return null;
+
+ return {
+ pos: word.to,
+ text: remainder
+ };
+}
+
+const ghostTextField = StateField.define({
+ create(state) {
+ return Decoration.none;
+ },
+
+ update(deco, tr) {
+ const decorationBuilder = new RangeSetBuilder();
+ const ghost = buildGhostText(tr.state);
+
+ if (ghost) {
+ decorationBuilder.add(
+ ghost.pos,
+ ghost.pos,
+ Decoration.widget({
+ widget: new GhostTextWidget(ghost.text),
+ side: 1
+ })
+ );
+ }
+
+ return decorationBuilder.finish();
+ },
+
+ provide: (field) => EditorView.decorations.from(field)
+});
+
+export const p5CompletionPreviewTheme = EditorView.theme({
+ '.cm-ghostCompletion': {
+ opacity: '0.55',
+ fontStyle: 'italic',
+ pointerEvents: 'none',
+ whiteSpace: 'pre'
+ }
+});
+
+export function p5CompletionPreview() {
+ return [ghostTextField, p5CompletionPreviewTheme];
+}
diff --git a/client/modules/IDE/components/Editor/p5JavaScript.js b/client/modules/IDE/components/Editor/p5JavaScript.js
new file mode 100644
index 0000000000..f729899dd9
--- /dev/null
+++ b/client/modules/IDE/components/Editor/p5JavaScript.js
@@ -0,0 +1,28 @@
+import { LanguageSupport } from '@codemirror/language';
+import { javascript } from '@codemirror/lang-javascript';
+import { p5Hinter } from '../../../../utils/p5-hinter';
+import { p5CompletionPreview } from './p5CompletionPreview';
+import contextAwareHinter from '../../../../utils/contextAwareHinter';
+
+function addCompletions(context) {
+ const word = context.matchBefore(/\w*/);
+
+ if (!word && !context.explicit) {
+ return null;
+ }
+
+ return contextAwareHinter(context, {
+ hints: p5Hinter
+ });
+}
+
+export default function p5JavaScript() {
+ const jsLang = javascript();
+ return new LanguageSupport(jsLang.language, [
+ jsLang.extension,
+ jsLang.language.data.of({
+ autocomplete: addCompletions
+ }),
+ p5CompletionPreview()
+ ]);
+}
diff --git a/client/modules/IDE/components/Editor/stateUtils.js b/client/modules/IDE/components/Editor/stateUtils.js
new file mode 100644
index 0000000000..fc20224eff
--- /dev/null
+++ b/client/modules/IDE/components/Editor/stateUtils.js
@@ -0,0 +1,525 @@
+import { EditorState, Compartment } from '@codemirror/state';
+import {
+ EditorView,
+ lineNumbers as lineNumbersExt,
+ highlightActiveLine,
+ highlightActiveLineGutter,
+ gutters,
+ keymap,
+ highlightSpecialChars,
+ drawSelection,
+ dropCursor,
+ rectangularSelection,
+ crosshairCursor
+} from '@codemirror/view';
+import {
+ foldGutter,
+ foldKeymap,
+ bracketMatching,
+ indentOnInput,
+ syntaxHighlighting
+} from '@codemirror/language';
+import {
+ autocompletion,
+ closeBrackets,
+ closeBracketsKeymap,
+ completionStatus,
+ selectedCompletionIndex
+} from '@codemirror/autocomplete';
+import {
+ highlightSelectionMatches,
+ search,
+ searchKeymap
+} from '@codemirror/search';
+import {
+ defaultKeymap,
+ history,
+ historyKeymap,
+ insertTab,
+ indentLess
+} from '@codemirror/commands';
+import { lintGutter } from '@codemirror/lint';
+import {
+ expandAbbreviation,
+ abbreviationTracker
+} from '@emmetio/codemirror6-plugin';
+
+import { css } from '@codemirror/lang-css';
+import { html } from '@codemirror/lang-html';
+import { json, jsonParseLinter } from '@codemirror/lang-json';
+import { xml } from '@codemirror/lang-xml';
+import { linter } from '@codemirror/lint';
+import { HTMLHint } from 'htmlhint';
+import { CSSLint } from 'csslint';
+import { emmetConfig } from '@emmetio/codemirror6-plugin';
+import { color as colorPicker } from '@connieye/codemirror-color-picker';
+import { esLint } from '@codemirror/lang-javascript';
+import { Linter as ESLinter } from 'eslint-linter-browserify';
+import { tidyCodeWithPrettier } from './tidier';
+import p5JavaScript from './p5JavaScript';
+import { highlightStyle } from './highlightStyle';
+import { errorDecorationStateField } from './consoleErrorDecoration';
+
+// ----- TODOS -----
+// - shader syntax highlighting
+
+/** Detects what mode the file is based on the name. */
+export function getFileMode(fileName) {
+ let mode;
+ if (fileName.match(/.+\.js$/i)) {
+ mode = 'javascript';
+ } else if (fileName.match(/.+\.css$/i)) {
+ mode = 'css';
+ } else if (fileName.match(/.+\.(html)$/i)) {
+ mode = 'html';
+ } else if (fileName.match(/.+\.(xml)$/i)) {
+ mode = 'xml';
+ } else if (fileName.match(/.+\.json$/i)) {
+ mode = 'application/json';
+ } else if (fileName.match(/.+\.(frag|glsl)$/i)) {
+ mode = 'x-shader/x-fragment';
+ } else if (fileName.match(/.+\.(vert|stl|mtl)$/i)) {
+ mode = 'x-shader/x-vertex';
+ } else {
+ mode = 'text/plain';
+ }
+ return mode;
+}
+
+function getFileLanguage(fileName) {
+ const fileMode = getFileMode(fileName);
+
+ switch (fileMode) {
+ case 'javascript':
+ return p5JavaScript;
+ case 'css':
+ return css;
+ case 'html':
+ return html;
+ case 'xml':
+ return xml;
+ case 'application/json':
+ return json;
+ default:
+ return null;
+ }
+}
+
+function makeCssLinter(callback) {
+ return (view) => {
+ const documentContent = view.state.doc.toString();
+ const { messages } = CSSLint.verify(documentContent, {});
+ const diagnostics = [];
+ messages.forEach((message) => {
+ if (!message) return;
+
+ const {
+ line: messageLine,
+ col: messageCharacter,
+ type: messageType,
+ message: messageText
+ } = message;
+ const cmLine = view.state.doc.line(messageLine);
+
+ // TODO: Can we to do the to/from smarter?
+ diagnostics.push({
+ from: cmLine.from + messageCharacter - 1,
+ to: cmLine.from + messageCharacter,
+ severity: messageType,
+ message: messageText
+ });
+ });
+
+ if (callback) callback(diagnostics);
+
+ return diagnostics;
+ };
+}
+
+// https://github.com/codemirror/codemirror5/blob/master/addon/lint/html-lint.js
+const HTMLHINT_OPTIONS = {
+ 'tagname-lowercase': true,
+ 'attr-lowercase': true,
+ 'attr-value-double-quotes': true,
+ 'doctype-first': false,
+ 'tag-pair': true,
+ 'spec-char-escape': true,
+ 'id-unique': true,
+ 'src-not-empty': true,
+ 'attr-no-duplication': true
+};
+
+function makeHtmlLinter(callback) {
+ return (view) => {
+ const documentContent = view.state.doc.toString();
+
+ const messages = HTMLHint.verify(documentContent, HTMLHINT_OPTIONS) || [];
+
+ const diagnostics = [];
+ messages.forEach((message) => {
+ if (!message) return;
+
+ const {
+ line: messageLine,
+ col: messageCharacter,
+ type: messageType,
+ message: messageText
+ } = message;
+ const cmLine = view.state.doc.line(messageLine);
+
+ // TODO: Can we to do the to/from smarter?
+ diagnostics.push({
+ from: cmLine.from + messageCharacter - 1,
+ to: cmLine.from + messageCharacter,
+ severity: messageType,
+ message: messageText
+ });
+ });
+
+ if (callback) callback(diagnostics);
+
+ return diagnostics;
+ };
+}
+
+const ESLINT_CONFIG = {
+ languageOptions: {
+ ecmaVersion: 2021
+ },
+ rules: {
+ semi: 'off',
+ eqeqeq: 'off'
+ }
+};
+
+const eslint = new ESLinter();
+
+function makeJsonLinter(callback) {
+ const baseJsonLinter = jsonParseLinter();
+ return (view) => {
+ const diagnostics = baseJsonLinter(view);
+ if (callback) callback(diagnostics);
+ return diagnostics;
+ };
+}
+
+function getFileLinter(fileName, callback) {
+ const fileMode = getFileMode(fileName);
+
+ switch (fileMode) {
+ case 'javascript':
+ return linter(esLint(eslint, ESLINT_CONFIG));
+ case 'html':
+ return linter(makeHtmlLinter(callback));
+ case 'css':
+ return linter(makeCssLinter(callback));
+ case 'application/json':
+ return linter(makeJsonLinter(callback));
+ default:
+ return null;
+ }
+}
+
+function getFileEmmetConfig(fileName) {
+ const fileMode = getFileMode(fileName);
+
+ switch (fileMode) {
+ case 'html':
+ return emmetConfig.of({ syntax: 'html' });
+ case 'css':
+ return emmetConfig.of({ syntax: 'css' });
+ default:
+ return null;
+ }
+}
+
+function focusOnReferenceArrow(view) {
+ if (completionStatus(view.state) !== 'active') return false;
+
+ const selectedIndex = selectedCompletionIndex(view.state);
+ if (selectedIndex == null || selectedIndex < 0) return false;
+
+ const tooltip = view.dom.querySelector('.cm-tooltip-autocomplete');
+ if (!tooltip) return false;
+
+ const options = tooltip.querySelectorAll('li.CodeMirror-hint');
+ const selectedOption = options[selectedIndex];
+ if (!selectedOption) return false;
+
+ const link = selectedOption.querySelector('.cm-completionRefLink');
+ if (!link) return false;
+
+ link.focus();
+ link.classList.add('focused-hint-link');
+
+ const cleanup = () => {
+ link.classList.remove('focused-hint-link');
+ link.removeEventListener('blur', cleanup);
+ };
+ link.addEventListener('blur', cleanup);
+
+ return true;
+}
+
+// Extra custom keymaps.
+// TODO: We need to add sublime mappings + other missing extra mappings here.
+const extraKeymaps = [
+ { key: 'ArrowRight', run: focusOnReferenceArrow },
+ { key: 'Tab', run: insertTab, shift: indentLess }
+];
+const emmetKeymaps = [{ key: 'Tab', run: expandAbbreviation }];
+
+/** Returns completion options configured for autocomplete. */
+export const createAutocompleteOptions = (referenceBaseUrl) => ({
+ tooltipClass: () => 'CodeMirror-hints',
+ closeOnBlur: false,
+ icons: false,
+
+ // handle css classes
+ optionClass(completion) {
+ let className = 'CodeMirror-hint';
+
+ if (completion.type) {
+ className += ` hint-type-${completion.type}`;
+ }
+
+ if (completion.p5DocPath) {
+ className += ' has-doc-link';
+ }
+
+ return className;
+ },
+
+ addToOptions: [
+ {
+ position: 60,
+ render(completion) {
+ const kind = document.createElement('span');
+ kind.className = 'cm-completionKind';
+ kind.textContent = completion.kindLabel || completion.type || '';
+ return kind;
+ }
+ },
+ {
+ position: 80,
+ render(completion, state, view) {
+ if (!completion.p5DocPath) return null;
+
+ // TODO: add in reference url version switching
+ const link = document.createElement('a');
+ link.className = 'cm-completionRefLink';
+ link.href = `${referenceBaseUrl}/reference/p5/${completion.p5DocPath}`;
+ link.target = '_blank';
+ link.rel = 'noopener noreferrer';
+ link.tabIndex = -1;
+ link.setAttribute('aria-label', `Open ${completion.label} reference`);
+
+ link.innerHTML = `
+
open ${completion.label} reference
+
➔
+ `;
+
+ link.addEventListener('mousedown', (event) => {
+ event.preventDefault();
+ event.stopPropagation();
+ });
+
+ link.addEventListener('click', (event) => {
+ event.stopPropagation();
+ });
+
+ link.addEventListener('keydown', (event) => {
+ if (event.key === 'ArrowLeft' || event.key === 'Escape') {
+ event.preventDefault();
+ event.stopPropagation();
+ link.classList.remove('focused-hint-link');
+ view.focus();
+ }
+ });
+
+ return link;
+ }
+ },
+ {
+ position: 100,
+ render(completion) {
+ if (!completion.blacklisted) return null;
+
+ const warning = document.createElement('div');
+ warning.className = 'cm-completionWarning';
+
+ const icon = document.createElement('span');
+ icon.className = 'cm-completionWarningIcon';
+ icon.setAttribute('aria-hidden', 'true');
+ icon.textContent = '⚠️';
+
+ const text = document.createElement('span');
+ text.className = 'cm-completionWarningText';
+ text.textContent = 'use with caution in this context';
+
+ warning.appendChild(icon);
+ warning.appendChild(text);
+
+ return warning;
+ }
+ }
+ ]
+});
+
+/**
+ * Creates a new CodeMirror editor state with configurations,
+ * extensions, and keymaps tailored to the file type and settings.
+ *
+ * Returns a "file state" object containing the CodeMirror state and compartments.
+ */
+export function createNewFileState(filename, document, settings) {
+ const {
+ linewrap,
+ lineNumbers,
+ autocomplete,
+ autocloseBracketsQuotes,
+ onUpdateLinting,
+ onViewUpdate,
+ referenceBaseUrl
+ } = settings;
+ const lineNumbersCpt = new Compartment();
+ const lineWrappingCpt = new Compartment();
+ const closeBracketsCpt = new Compartment();
+ const autocompleteCpt = new Compartment();
+
+ // Depending on the file mode, we have a different tidier function.
+ const mode = getFileMode(filename);
+ extraKeymaps.push({
+ key: `Shift-Mod-F`,
+ run: (cmView) => tidyCodeWithPrettier(cmView, mode)
+ });
+
+ const keymaps = [
+ extraKeymaps,
+ closeBracketsKeymap,
+ defaultKeymap,
+ historyKeymap,
+ foldKeymap,
+ searchKeymap
+ ];
+
+ // https://github.com/codemirror/basic-setup/blob/main/src/codemirror.ts
+ const extensions = [
+ // The first few extensions can be toggled on or off.
+ lineNumbersCpt.of(lineNumbers ? lineNumbersExt() : []),
+ lineWrappingCpt.of(linewrap ? EditorView.lineWrapping : []),
+ closeBracketsCpt.of(autocloseBracketsQuotes ? closeBrackets() : []),
+ autocompleteCpt.of(
+ autocomplete
+ ? autocompletion(createAutocompleteOptions(referenceBaseUrl))
+ : []
+ ),
+
+ // Everything below here should always be on.
+ history(),
+ search(),
+ // Highlight extensions
+ highlightActiveLine(),
+ highlightActiveLineGutter(),
+ highlightSpecialChars(),
+ highlightSelectionMatches(),
+ syntaxHighlighting(highlightStyle),
+ // Selection extensions
+ drawSelection(),
+ rectangularSelection(),
+ dropCursor(),
+ crosshairCursor(),
+ EditorState.allowMultipleSelections.of(true),
+ // Gutter extensions
+ gutters({ fixed: false }),
+ foldGutter(),
+ // Misc extensions
+ indentOnInput(),
+ bracketMatching(),
+ errorDecorationStateField,
+
+ // Setup the event listeners on the CodeMirror instance.
+ EditorView.updateListener.of(onViewUpdate)
+ ];
+
+ // Only enable the color picker for Javascript and CSS, which
+ // have both been tested.
+ const fileMode = getFileMode(filename);
+ if (fileMode === 'javascript' || fileMode === 'css') {
+ extensions.push(colorPicker);
+ }
+
+ const fileLanguage = getFileLanguage(filename);
+ const fileLinter = getFileLinter(filename, onUpdateLinting);
+ const fileEmmetConfig = getFileEmmetConfig(filename);
+
+ if (fileLanguage) {
+ extensions.push(fileLanguage());
+ }
+ if (fileLinter) {
+ extensions.push(fileLinter);
+ extensions.push(lintGutter());
+ }
+
+ // If it's HTML or CSS, we add some emmet-specific configs.
+ if (fileEmmetConfig) {
+ extensions.push(fileEmmetConfig);
+ extensions.push(abbreviationTracker());
+ keymaps.push(emmetKeymaps);
+ }
+
+ // Now add the keymaps...
+ extensions.push(keymap.of(keymaps.flat()));
+
+ // Create the state with document content if we have it.
+ const stateOptions = {
+ extensions
+ };
+ if (document) {
+ stateOptions.doc = document;
+ }
+
+ const cmState = EditorState.create(stateOptions);
+ return {
+ cmState,
+ lineNumbersCpt,
+ lineWrappingCpt,
+ closeBracketsCpt,
+ autocompleteCpt
+ };
+}
+
+/**
+ * Given a reconfigure effect, this function will update all
+ * of the file states.
+ *
+ * We need to do this whenever the settings like line numbers
+ * change, so it will get called in the useEffect hooks.
+ */
+export function updateFileStates({
+ fileStates,
+ cmView,
+ file: currentFile,
+ reconfigureEffect
+}) {
+ if (!fileStates) return;
+
+ Object.entries(fileStates).forEach(([fileId, fileState]) => {
+ // Either grab the current state from the view or saved in the fileStates.
+ let { cmState } = fileState;
+ if (fileId === currentFile.id) {
+ cmState = cmView.state;
+ }
+
+ // Apply the new effects and grab the new state.
+ const { state: newCmState } = cmState.update({
+ effects: reconfigureEffect(fileState)
+ });
+
+ // Save the new states and update the view for the currently open file.
+ fileStates[fileId].cmState = newCmState;
+ if (fileId === currentFile.id) {
+ cmView.setState(newCmState);
+ }
+ });
+}
diff --git a/client/modules/IDE/components/Editor/tidier.test.ts b/client/modules/IDE/components/Editor/tidier.test.ts
new file mode 100644
index 0000000000..babb27c8b2
--- /dev/null
+++ b/client/modules/IDE/components/Editor/tidier.test.ts
@@ -0,0 +1,67 @@
+import { EditorState } from '@codemirror/state';
+import { EditorView } from '@codemirror/view';
+import { tidyCodeWithPrettier } from './tidier';
+
+describe('tidyCodeWithPrettier', () => {
+ function createEditor(content: string) {
+ const state = EditorState.create({ doc: content });
+ return new EditorView({ state });
+ }
+
+ function getDocText(cmView: EditorView) {
+ return cmView.state.doc.toString();
+ }
+
+ function getCursor(cmView: EditorView) {
+ return cmView.state.selection.main.head;
+ }
+
+ it('formats JavaScript correctly and keeps cursor stable', () => {
+ const messyJs = `function foo(){console.log("hi")}`;
+ const cmView = createEditor(messyJs);
+ cmView.dispatch({ selection: { anchor: 14 } });
+
+ tidyCodeWithPrettier(cmView, 'javascript');
+
+ const expectedJs = 'function foo() {\n console.log("hi");\n}\n';
+ expect(getDocText(cmView)).toBe(expectedJs);
+
+ const newCursor = getCursor(cmView);
+ expect(newCursor).toBeGreaterThan(0);
+ });
+
+ it('formats HTML correctly and keeps cursor stable', () => {
+ const messyHtml = `hello`;
+ const cmView = createEditor(messyHtml);
+ tidyCodeWithPrettier(cmView, 'html');
+
+ const expectedHtml = '\n \n hello\n \n\n';
+ expect(getDocText(cmView)).toBe(expectedHtml);
+ });
+
+ it('formats CSS correctly and keeps cursor stable', () => {
+ const messyCss = `body{margin:0;padding:0;}`;
+ const cmView = createEditor(messyCss);
+
+ tidyCodeWithPrettier(cmView, 'css');
+
+ const expectedCss = 'body {\n margin: 0;\n padding: 0;\n}\n';
+ expect(getDocText(cmView)).toBe(expectedCss);
+ });
+
+ it('handles an empty document without errors', () => {
+ const cmView = createEditor('');
+ expect(() => tidyCodeWithPrettier(cmView, 'javascript')).not.toThrow();
+ expect(getDocText(cmView)).toBe('');
+ });
+
+ it('leaves the cursor unmoved if the code is already formatted', () => {
+ const cleanCode = 'function foo() {}\n';
+ const cmView = createEditor(cleanCode);
+ cmView.dispatch({ selection: { anchor: 5 } });
+
+ tidyCodeWithPrettier(cmView, 'javascript');
+
+ expect(getCursor(cmView)).toBe(5);
+ });
+});
diff --git a/client/modules/IDE/components/Editor/tidier.ts b/client/modules/IDE/components/Editor/tidier.ts
new file mode 100644
index 0000000000..41fdb24572
--- /dev/null
+++ b/client/modules/IDE/components/Editor/tidier.ts
@@ -0,0 +1,44 @@
+import prettier from 'prettier/standalone';
+import babelParser from 'prettier/parser-babel';
+import htmlParser from 'prettier/parser-html';
+import cssParser from 'prettier/parser-postcss';
+import type { EditorView } from '@codemirror/view';
+import type { Plugin } from 'prettier';
+
+type ParserTypes = 'babel' | 'html' | 'css';
+type FormatMode = 'html' | 'css' | 'javascript';
+
+function prettierFormatWithCursor(
+ parser: ParserTypes,
+ plugins: Plugin[],
+ cmView: EditorView
+) {
+ const { doc } = cmView.state;
+ const cursorOffset = cmView.state.selection.main.head;
+ const {
+ formatted,
+ cursorOffset: newCursorOffset
+ } = prettier.formatWithCursor(doc.toString(), {
+ cursorOffset,
+ parser,
+ plugins
+ });
+
+ cmView.dispatch({
+ changes: { from: 0, to: doc.length, insert: formatted },
+ selection: { anchor: newCursorOffset }
+ });
+
+ cmView.focus();
+}
+
+/** Runs prettier on the codemirror instance, depending on the mode. */
+export function tidyCodeWithPrettier(cmView: EditorView, mode: FormatMode) {
+ if (mode === 'javascript') {
+ prettierFormatWithCursor('babel', [babelParser], cmView);
+ } else if (mode === 'css') {
+ prettierFormatWithCursor('css', [cssParser], cmView);
+ } else if (mode === 'html') {
+ prettierFormatWithCursor('html', [htmlParser], cmView);
+ }
+}
diff --git a/client/modules/IDE/components/Header/MobileNav.jsx b/client/modules/IDE/components/Header/MobileNav.jsx
index 2ef0ff3026..b055b62be8 100644
--- a/client/modules/IDE/components/Header/MobileNav.jsx
+++ b/client/modules/IDE/components/Header/MobileNav.jsx
@@ -427,7 +427,7 @@ const MoreMenu = () => {
{t('Nav.Edit.TidyCode')}
-
+
{t('Nav.Edit.Find')}
{t('Nav.Sketch.Title')}
diff --git a/client/modules/IDE/components/Header/Nav.jsx b/client/modules/IDE/components/Header/Nav.jsx
index 48e0935ae9..0ca5f67961 100644
--- a/client/modules/IDE/components/Header/Nav.jsx
+++ b/client/modules/IDE/components/Header/Nav.jsx
@@ -169,8 +169,6 @@ const ProjectMenu = () => {
shareSketch
} = useSketchActions();
- const replaceCommand =
- metaKey === 'Ctrl' ? `${metaKeyName}+H` : `${metaKeyName}+⌥+F`;
const newFileCommand =
metaKey === 'Ctrl' ? `${metaKeyName}+Alt+N` : `${metaKeyName}+⌥+N`;
@@ -268,14 +266,10 @@ const ProjectMenu = () => {
{t('Nav.Edit.TidyCode')}
{metaKeyName}+Shift+F
-
+
{t('Nav.Edit.Find')}
{metaKeyName}+F
-
- {t('Nav.Edit.Replace')}
- {replaceCommand}
-
-
- Replace
-
- Ctrl+H
-
-
{
@@ -15,8 +16,11 @@ const Pagination = ({
const { t } = useTranslation();
- const startSketch = (page - 1) * limit + 1;
- const endSketch = Math.min(page * limit, totalSketches);
+ const totalItems = Number.isFinite(totalSketches)
+ ? totalSketches
+ : totalCollections;
+ const startItem = totalItems > 0 ? (page - 1) * limit + 1 : 0;
+ const endItem = totalItems > 0 ? Math.min(page * limit, totalItems) : 0;
return (
@@ -35,11 +39,12 @@ const Pagination = ({
- {startSketch} - {endSketch}
+ {startItem} - {endItem}
{' '}
- {t('Pagination.Of')} {totalSketches}
+ {t('Pagination.Of')} {totalItems}
+
1) return;
- // By default, don't allow completion when something is selected.
- // A hint function can have a `supportsSelection` property to
- // indicate that it can handle selections.
- if (this.somethingSelected()) {
- if (!options.hint.supportsSelection) return;
- // Don't try with cross-line selections
- // if selection spans multiple lines, bail out
- for (var i = 0; i < selections.length; i++)
- if (selections[i].head.line != selections[i].anchor.line) return;
- }
-
- if (this.state.completionActive) this.state.completionActive.close(); // close an already active autocomplete session if active
- // create a new completion object and saves it to this.state.completionActive
- var completion = (this.state.completionActive = new Completion(
- this,
- options
- ));
- if (!completion.options.hint) return; // safety check to ensure hint is valid
-
- CodeMirror.signal(this, 'startCompletion', this); // emits a signal; fires a startCompletion event on editor instance
- completion.update(true);
- });
-
- CodeMirror.defineExtension('closeHint', function () {
- if (this.state.completionActive) this.state.completionActive.close();
- });
-
- // defines a constructor function
- function Completion(cm, options) {
- this.cm = cm;
- this.options = options;
- this.widget = null; // will hold a reference to the dropdown menu that shows suggestions
- this.debounce = 0;
- this.tick = 0;
- this.startPos = this.cm.getCursor('start'); // startPos is a {line,ch} object used to remember where hinting started
- // startLen is the len of the line minus length of any selected text
- this.startLen =
- this.cm.getLine(this.startPos.line).length -
- this.cm.getSelection().length;
-
- if (this.options.updateOnCursorActivity) {
- var self = this; // stores ref to this as self so it can be accessed inside the nested function
- // adds an event listener to the editor; called when the cursor moves
- cm.on(
- 'cursorActivity',
- (this.activityFunc = function () {
- self.cursorActivity();
- })
- );
- }
- }
-
- var requestAnimationFrame =
- window.requestAnimationFrame ||
- function (fn) {
- return setTimeout(fn, 1000 / 60);
- };
- var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout;
-
- Completion.prototype = {
- close: function () {
- if (!this.active()) return;
- this.cm.state.completionActive = null;
- this.tick = null;
- // removes the current activity listener
- if (this.options.updateOnCursorActivity) {
- this.cm.off('cursorActivity', this.activityFunc);
- }
- // signals and removes the widget
- if (this.widget && this.data) CodeMirror.signal(this.data, 'close');
- if (this.widget) this.widget.close();
- // emits a completition end event
- CodeMirror.signal(this.cm, 'endCompletion', this.cm);
- },
-
- active: function () {
- return this.cm.state.completionActive == this;
- },
-
- pick: function (data, i) {
- // selects an item from the suggestion list
- var completion = data.list[i],
- self = this;
-
- this.cm.operation(function () {
- const name = completion.item?.text;
-
- if (completion.hint) {
- completion.hint(self.cm, data, completion);
- } else {
- self.cm.replaceRange(
- getText(completion),
- completion.from || data.from,
- completion.to || data.to,
- 'complete'
- );
- }
- // signals that a hint was picked and scrolls to it
- CodeMirror.signal(data, 'pick', completion);
- self.cm.scrollIntoView();
- });
- // closes widget if closeOnPick is enabled
- if (this.options.closeOnPick) {
- this.close();
- }
- },
-
- cursorActivity: function () {
- // if a debounce is scheduled, cancel it to avoid outdated updates
- if (this.debounce) {
- cancelAnimationFrame(this.debounce);
- this.debounce = 0;
- }
-
- var identStart = this.startPos;
- if (this.data) {
- identStart = this.data.from;
- }
-
- var pos = this.cm.getCursor(),
- line = this.cm.getLine(pos.line);
- if (
- pos.line != this.startPos.line ||
- line.length - pos.ch != this.startLen - this.startPos.ch ||
- pos.ch < identStart.ch ||
- this.cm.somethingSelected() ||
- !pos.ch ||
- this.options.closeCharacters.test(line.charAt(pos.ch - 1))
- ) {
- this.close();
- } else {
- var self = this;
- this.debounce = requestAnimationFrame(function () {
- self.update();
- });
- if (this.widget) this.widget.disable();
- }
- },
-
- update: function (first) {
- if (this.tick == null) return;
- var self = this,
- myTick = ++this.tick;
- fetchHints(this.options.hint, this.cm, this.options, function (data) {
- if (self.tick == myTick) self.finishUpdate(data, first);
- });
- },
-
- finishUpdate: function (data, first) {
- if (this.data) CodeMirror.signal(this.data, 'update');
-
- var picked =
- (this.widget && this.widget.picked) ||
- (first && this.options.completeSingle);
- if (this.widget) this.widget.close();
-
- this.data = data;
-
- if (data && data.list.length) {
- if (picked && data.list.length == 1) {
- this.pick(data, 0);
- } else {
- this.widget = new Widget(this, data);
- CodeMirror.signal(data, 'shown');
- }
- }
- }
- };
-
- function parseOptions(cm, pos, options) {
- var editor = cm.options.hintOptions;
- var out = {};
- // copies all default hint settings into out
- for (var prop in defaultOptions) out[prop] = defaultOptions[prop];
- if (editor)
- for (var prop in editor)
- if (editor[prop] !== undefined) out[prop] = editor[prop];
- if (options)
- for (var prop in options)
- if (options[prop] !== undefined) out[prop] = options[prop];
- if (out.hint.resolve) out.hint = out.hint.resolve(cm, pos);
- return out;
- }
- // extracts the visible text from a completion entry
- function getText(completion) {
- if (typeof completion === 'string') return completion;
- else return completion.item.text;
- }
-
- // builds a key mapping object to define keyboard behavior for autocomplete
- function buildKeyMap(completion, handle) {
- var baseMap = {
- Up: function () {
- handle.moveFocus(-1);
- },
- Down: function () {
- handle.moveFocus(1);
- },
- PageUp: function () {
- handle.moveFocus(-handle.menuSize() + 1, true);
- },
- PageDown: function () {
- handle.moveFocus(handle.menuSize() - 1, true);
- },
- Home: function () {
- handle.setFocus(0);
- },
- End: function () {
- handle.setFocus(handle.length - 1);
- },
- Enter: handle.pick,
- Tab: handle.pick,
- Esc: handle.close
- };
- // checks if the user is on macOS and adds shortcuts accordingly
- var mac = /Mac/.test(navigator.platform);
-
- if (mac) {
- baseMap['Ctrl-P'] = function () {
- handle.moveFocus(-1);
- };
- baseMap['Ctrl-N'] = function () {
- handle.moveFocus(1);
- };
- }
-
- // user defined custom key bindings
- var custom = completion.options.customKeys;
- var ourMap = custom ? {} : baseMap;
- function addBinding(key, val) {
- var bound;
- if (typeof val != 'string')
- bound = function (cm) {
- return val(cm, handle);
- };
- // This mechanism is deprecated
- else if (baseMap.hasOwnProperty(val)) bound = baseMap[val];
- else bound = val;
- ourMap[key] = bound;
- }
- // apply all custom key bindings and extraKeys
- if (custom)
- for (var key in custom)
- if (custom.hasOwnProperty(key)) addBinding(key, custom[key]);
- var extra = completion.options.extraKeys;
- if (extra)
- for (var key in extra)
- if (extra.hasOwnProperty(key)) addBinding(key, extra[key]);
- return ourMap;
- }
-
- // hintsElement is the parent for hints and el is the clicked element within that container
- function displayHint(name, type, p5, isBlacklistedFunction, referenceBaseUrl) {
- const base = referenceBaseUrl || 'https://p5js.org';
- const refName = typeof p5 === 'string' ? p5 : name;
-
- const linkOrPlaceholder = p5
- ? `
- open ${name} reference
- ➔
- `
- : `
- no reference for ${name}
- `;
-
- const hintHTML = `
- ${name}
- ${type}
- ${linkOrPlaceholder}
-
`;
-
- if (isBlacklistedFunction) {
- return `
- ${hintHTML}
-
⚠️use with caution in this context
-
`;
- } else {
- return `${hintHTML}
`;
- }
- }
-
-
- function getInlineHintSuggestion(cm, focus, token) {
- let tokenLength = token.string.length;
- if (token.string === '.') {
- tokenLength -= 1;
- }
- const name = focus.item?.text;
-
- const suggestionItem = focus.item;
- // builds the remainder of the suggestion excluding what user already typed
- const baseCompletion = `${suggestionItem.text.slice(
- tokenLength
- )}`;
- if (suggestionItem.type !== 'fun') return baseCompletion;
-
- // for functions
- return (
- baseCompletion +
- '(' +
- (suggestionItem.params && suggestionItem.params.length
- ? suggestionItem.params.map(({ p, o }) => (o ? `[${p}]` : p)).join(', ')
- : '') +
- ')'
- );
- }
-
- // clears existing inline hint (like the part is suggested)
- function removeInlineHint(cm) {
- if (cm.state.inlineHint) {
- cm.state.inlineHint.clear();
- cm.state.inlineHint = null;
- }
- }
-
- function changeInlineHint(cm, focus) {
- removeInlineHint(cm);
-
- const cursor = cm.getCursor();
- const token = cm.getTokenAt(cursor);
-
- if (token && focus.item) {
- const suggestionHTML = getInlineHintSuggestion(cm, focus, token);
-
- const widgetElement = document.createElement('span');
- widgetElement.className = 'autocomplete-inline-hinter';
- widgetElement.innerHTML = suggestionHTML;
-
- const widget = cm.setBookmark(cursor, { widget: widgetElement });
- cm.state.inlineHint = widget;
- cm.setCursor(cursor);
- }
- }
-
- // defines the autocomplete dropdown ui; renders the suggestions
- // completion = the autocomplete context having cm and options
- // data = object with the list of suggestions
- function Widget(completion, data) {
- this.id = 'cm-complete-' + Math.floor(Math.random(1e6));
- this.completion = completion;
- this.data = data;
- this.picked = false;
- var widget = this,
- cm = completion.cm;
- var ownerDocument = cm.getInputField().ownerDocument;
- var parentWindow = ownerDocument.defaultView || ownerDocument.parentWindow;
-
- var fontSize = completion.options._fontSize;
-
- var hints = (this.hints = ownerDocument.createElement('ul'));
- hints.setAttribute('role', 'listbox');
- hints.setAttribute('aria-expanded', 'true');
- hints.id = this.id;
- var theme = completion.cm.options.theme;
- hints.className = 'CodeMirror-hints ' + theme;
- this.selectedHint = data.selectedHint || 0;
-
- const referenceBaseUrl = completion.options.referenceBaseUrl || 'https://p5js.org';
-
- // Show inline hint
- changeInlineHint(cm, data.list[this.selectedHint]);
-
- var completions = data.list;
- for (var i = 0; i < completions.length; ++i) {
- const cur = completions[i];
-
- const elt = ownerDocument.createElement('li');
- elt.className =
- HINT_ELEMENT_CLASS +
- (i !== this.selectedHint ? '' : ' ' + ACTIVE_HINT_ELEMENT_CLASS) +
- (cur.isBlacklisted ? ' blacklisted' : '');
-
- if (cur.className != null)
- elt.className = cur.className + ' ' + elt.className;
-
- if (i === this.selectedHint) elt.setAttribute('aria-selected', 'true');
- elt.id = this.id + '-' + i;
- elt.setAttribute('role', 'option');
- elt.hintId = i;
-
- if (cur.render) {
- cur.render(elt, data, cur);
- } else {
- const name = getText(cur);
- if (cur.item && cur.item.type) {
- cur.displayText = displayHint(
- name,
- cur.item.type,
- cur.item.p5,
- cur.isBlacklisted,
- referenceBaseUrl
- );
- }
-
- elt.innerHTML =
- cur.displayText || `${name}`;
- }
-
- hints.appendChild(elt);
- }
-
- var container = completion.options.container || ownerDocument.body;
- var pos = cm.cursorCoords(
- completion.options.alignWithWord ? data.from : null
- );
- var left = pos.left,
- top = pos.bottom,
- below = true;
- var offsetLeft = 0,
- offsetTop = 0;
- if (container !== ownerDocument.body) {
- // We offset the cursor position because left and top are relative to the offsetParent's top left corner.
- var isContainerPositioned =
- ['absolute', 'relative', 'fixed'].indexOf(
- parentWindow.getComputedStyle(container).position
- ) !== -1;
- var offsetParent = isContainerPositioned
- ? container
- : container.offsetParent;
- var offsetParentPosition = offsetParent.getBoundingClientRect();
- var bodyPosition = ownerDocument.body.getBoundingClientRect();
- offsetLeft =
- offsetParentPosition.left - bodyPosition.left - offsetParent.scrollLeft;
- offsetTop =
- offsetParentPosition.top - bodyPosition.top - offsetParent.scrollTop;
- }
- hints.style.left = left - offsetLeft + 'px';
- hints.style.top = top - offsetTop + 'px';
-
- // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
- var winW =
- parentWindow.innerWidth ||
- Math.max(
- ownerDocument.body.offsetWidth,
- ownerDocument.documentElement.offsetWidth
- );
- var winH =
- parentWindow.innerHeight ||
- Math.max(
- ownerDocument.body.offsetHeight,
- ownerDocument.documentElement.offsetHeight
- );
- container.appendChild(hints);
- cm.getInputField().setAttribute('aria-autocomplete', 'list');
- cm.getInputField().setAttribute('aria-owns', this.id);
- cm.getInputField().setAttribute(
- 'aria-activedescendant',
- this.id + '-' + this.selectedHint
- );
-
- var box = completion.options.moveOnOverlap
- ? hints.getBoundingClientRect()
- : new DOMRect();
- var scrolls = completion.options.paddingForScrollbar
- ? hints.scrollHeight > hints.clientHeight + 1
- : false;
-
- // Compute in the timeout to avoid reflow on init
- var startScroll;
- setTimeout(function () {
- startScroll = cm.getScrollInfo();
- });
-
- var overlapY = box.bottom - winH;
- if (overlapY > 0) {
- var height = box.bottom - box.top,
- curTop = pos.top - (pos.bottom - box.top);
- if (curTop - height > 0) {
- // Fits above cursor
- hints.style.top = (top = pos.top - height - offsetTop) + 'px';
- below = false;
- } else if (height > winH) {
- hints.style.height = winH - 5 + 'px';
- hints.style.top = (top = pos.bottom - box.top - offsetTop) + 'px';
- var cursor = cm.getCursor();
- if (data.from.ch != cursor.ch) {
- pos = cm.cursorCoords(cursor);
- hints.style.left = (left = pos.left - offsetLeft) + 'px';
- box = hints.getBoundingClientRect();
- }
- }
- }
- var overlapX = box.right - winW;
- if (scrolls) overlapX += cm.display.nativeBarWidth;
- if (overlapX > 0) {
- if (box.right - box.left > winW) {
- hints.style.width = winW - 5 + 'px';
- overlapX -= box.right - box.left - winW;
- }
- hints.style.left = (left = pos.left - overlapX - offsetLeft) + 'px';
- }
- // if (scrolls) for (var node = hints.firstChild; node; node = node.nextSibling)
- // node.style.paddingRight = cm.display.nativeBarWidth + "px"
-
- cm.addKeyMap(
- (this.keyMap = buildKeyMap(completion, {
- moveFocus: function (n, avoidWrap) {
- return widget.changeActive(widget.selectedHint + n, avoidWrap);
- },
- setFocus: function (n) {
- return widget.changeActive(n);
- },
- menuSize: function () {
- return widget.screenAmount();
- },
- length: completions.length,
- close: function () {
- completion.close();
- },
- pick: function () {
- widget.pick();
- },
- data: data
- }))
- );
-
- if (completion.options.closeOnUnfocus) {
- var closingOnBlur;
- cm.on(
- 'blur',
- (this.onBlur = function () {
- closingOnBlur = setTimeout(function () {
- completion.close();
- }, 100);
- })
- );
- cm.on(
- 'focus',
- (this.onFocus = function () {
- clearTimeout(closingOnBlur);
- })
- );
- }
-
- cm.on(
- 'scroll',
- (this.onScroll = function () {
- var curScroll = cm.getScrollInfo(),
- editor = cm.getWrapperElement().getBoundingClientRect();
- if (!startScroll) startScroll = cm.getScrollInfo();
- var newTop = top + startScroll.top - curScroll.top;
- var point =
- newTop -
- (parentWindow.pageYOffset ||
- (ownerDocument.documentElement || ownerDocument.body).scrollTop);
- if (!below) point += hints.offsetHeight;
- if (point <= editor.top || point >= editor.bottom)
- return completion.close();
- hints.style.top = newTop + 'px';
- hints.style.left = left + startScroll.left - curScroll.left + 'px';
- })
- );
-
- function getHintElement(container, el) {
- while (el && el !== container && el.hintId == null) {
- el = el.parentNode;
- }
- return el;
- }
-
- CodeMirror.on(hints, 'dblclick', function (e) {
- var t = getHintElement(hints, e.target || e.srcElement);
- if (t && t.hintId != null) {
- widget.changeActive(t.hintId);
- widget.pick();
- }
- });
-
- CodeMirror.on(hints, 'click', function (e) {
- var t = getHintElement(hints, e.target || e.srcElement);
- if (t && t.hintId != null) {
- widget.changeActive(t.hintId);
- if (completion.options.completeOnSingleClick) widget.pick();
- }
- });
-
- CodeMirror.on(hints, 'mousedown', function () {
- setTimeout(function () {
- cm.focus();
- }, 20);
- });
-
- // The first hint doesn't need to be scrolled to on init
- var selectedHintRange = this.getSelectedHintRange();
- if (selectedHintRange.from !== 0 || selectedHintRange.to !== 0) {
- this.scrollToActive();
- }
-
- CodeMirror.signal(
- data,
- 'select',
- completions[this.selectedHint],
- hints.childNodes[this.selectedHint]
- );
- return true;
- }
-
- Widget.prototype = {
- close: function () {
- if (this.completion.widget != this) return;
- this.completion.widget = null;
- if (this.hints.parentNode) this.hints.parentNode.removeChild(this.hints);
- this.completion.cm.removeKeyMap(this.keyMap);
- var input = this.completion.cm.getInputField();
- input.removeAttribute('aria-activedescendant');
- input.removeAttribute('aria-owns');
-
- var cm = this.completion.cm;
- if (this.completion.options.closeOnUnfocus) {
- cm.off('blur', this.onBlur);
- cm.off('focus', this.onFocus);
- }
- cm.off('scroll', this.onScroll);
-
- removeInlineHint(cm);
- },
-
- disable: function () {
- this.completion.cm.removeKeyMap(this.keyMap);
- var widget = this;
- this.keyMap = {
- Enter: function () {
- widget.picked = true;
- }
- };
- this.completion.cm.addKeyMap(this.keyMap);
- },
-
- pick: function () {
- this.completion.pick(this.data, this.selectedHint);
- },
-
- changeActive: function (i, avoidWrap) {
- if (i >= this.data.list.length)
- i = avoidWrap ? this.data.list.length - 1 : 0;
- else if (i < 0) i = avoidWrap ? 0 : this.data.list.length - 1;
-
- if (this.selectedHint == i) {
- changeInlineHint(this.completion.cm, this.data.list[this.selectedHint]);
- return this.data.list[this.selectedHint];
- }
-
- var node = this.hints.childNodes[this.selectedHint];
- if (node) {
- node.className = node.className.replace(
- ' ' + ACTIVE_HINT_ELEMENT_CLASS,
- ''
- );
- node.removeAttribute('aria-selected');
- }
- node = this.hints.childNodes[(this.selectedHint = i)];
- node.className += ' ' + ACTIVE_HINT_ELEMENT_CLASS;
- node.setAttribute('aria-selected', 'true');
- this.completion.cm
- .getInputField()
- .setAttribute('aria-activedescendant', node.id);
- this.scrollToActive();
- CodeMirror.signal(
- this.data,
- 'select',
- this.data.list[this.selectedHint],
- node
- );
-
- changeInlineHint(this.completion.cm, this.data.list[this.selectedHint]);
- return this.data.list[this.selectedHint];
- },
-
- scrollToActive: function () {
- var selectedHintRange = this.getSelectedHintRange();
- var node1 = this.hints.childNodes[selectedHintRange.from];
- var node2 = this.hints.childNodes[selectedHintRange.to];
- var firstNode = this.hints.firstChild;
- if (node1.offsetTop < this.hints.scrollTop)
- this.hints.scrollTop = node1.offsetTop - firstNode.offsetTop;
- else if (
- node2.offsetTop + node2.offsetHeight >
- this.hints.scrollTop + this.hints.clientHeight
- )
- this.hints.scrollTop =
- node2.offsetTop +
- node2.offsetHeight -
- this.hints.clientHeight +
- firstNode.offsetTop;
- },
-
- screenAmount: function () {
- return (
- Math.floor(
- this.hints.clientHeight / this.hints.firstChild.offsetHeight
- ) || 1
- );
- },
-
- getSelectedHintRange: function () {
- var margin = this.completion.options.scrollMargin || 0;
- return {
- from: Math.max(0, this.selectedHint - margin),
- to: Math.min(this.data.list.length - 1, this.selectedHint + margin)
- };
- }
- };
-
- function applicableHelpers(cm, helpers) {
- if (!cm.somethingSelected()) return helpers;
- var result = [];
- for (var i = 0; i < helpers.length; i++)
- if (helpers[i].supportsSelection) result.push(helpers[i]);
- return result;
- }
-
- function fetchHints(hint, cm, options, callback) {
- if (hint.async) {
- hint(cm, callback, options);
- } else {
- var result = hint(cm, options);
- if (result && result.then) result.then(callback);
- else callback(result);
- }
- }
-
- function resolveAutoHints(cm, pos) {
- var helpers = cm.getHelpers(pos, 'hint'),
- words;
- if (helpers.length) {
- var resolved = function (cm, callback, options) {
- var app = applicableHelpers(cm, helpers);
- function run(i) {
- if (i == app.length) return callback(null);
- fetchHints(app[i], cm, options, function (result) {
- if (result && result.list.length > 0) callback(result);
- else run(i + 1);
- });
- }
- run(0);
- };
- resolved.async = true;
- resolved.supportsSelection = true;
- return resolved;
- } else if ((words = cm.getHelper(cm.getCursor(), 'hintWords'))) {
- return function (cm) {
- return CodeMirror.hint.fromList(cm, { words: words });
- };
- } else if (CodeMirror.hint.anyword) {
- return function (cm, options) {
- return CodeMirror.hint.anyword(cm, options);
- };
- } else {
- return function () {};
- }
- }
-
- CodeMirror.registerHelper('hint', 'auto', {
- resolve: resolveAutoHints
- });
-
- CodeMirror.registerHelper('hint', 'fromList', function (cm, options) {
- var cur = cm.getCursor(),
- token = cm.getTokenAt(cur);
- var term,
- from = CodeMirror.Pos(cur.line, token.start),
- to = cur;
- if (
- token.start < cur.ch &&
- /\w/.test(token.string.charAt(cur.ch - token.start - 1))
- ) {
- term = token.string.substr(0, cur.ch - token.start);
- } else {
- term = '';
- from = cur;
- }
- var found = [];
- for (var i = 0; i < options.words.length; i++) {
- var word = options.words[i];
- if (word.slice(0, term.length) == term) found.push(word);
- }
-
- if (found.length) return { list: found, from: from, to: to };
- });
-
- CodeMirror.commands.autocomplete = CodeMirror.showHint;
-
- var defaultOptions = {
- hint: CodeMirror.hint.auto,
- completeSingle: true,
- alignWithWord: true,
- closeCharacters: /[\s()\[\]{};:>,]/,
- closeOnPick: true,
- closeOnUnfocus: true,
- updateOnCursorActivity: true,
- completeOnSingleClick: true,
- container: null,
- customKeys: null,
- extraKeys: null,
- paddingForScrollbar: true,
- moveOnOverlap: true
- };
-
- CodeMirror.defineOption('hintOptions', null);
-});
diff --git a/client/modules/IDE/reducers/CollectionsListCollection.js b/client/modules/IDE/reducers/CollectionsListCollection.js
new file mode 100644
index 0000000000..4a347f7255
--- /dev/null
+++ b/client/modules/IDE/reducers/CollectionsListCollection.js
@@ -0,0 +1,47 @@
+import * as ActionTypes from '../../../constants';
+
+const initialState = {
+ collections: [],
+ metadata: {
+ page: 1,
+ totalPages: 1,
+ totalCollections: 0,
+ limit: 10,
+ hasPagination: false
+ }
+};
+
+export default function collectionsListCollections(
+ state = initialState,
+ action
+) {
+ switch (action.type) {
+ case ActionTypes.SET_COLLECTIONS_FOR_COLLECTION_LIST:
+ return {
+ ...state,
+ collections: action.collections?.collections ?? [],
+ metadata: action.collections?.metadata ?? initialState.metadata
+ };
+
+ case ActionTypes.DELETE_COLLECTION:
+ return {
+ ...state,
+ collections: state.collections.filter(
+ ({ id }) => action.collectionId !== id
+ )
+ };
+
+ case ActionTypes.EDIT_COLLECTION:
+ case ActionTypes.ADD_TO_COLLECTION:
+ case ActionTypes.REMOVE_FROM_COLLECTION:
+ return {
+ ...state,
+ collections: state.collections.map((collection) =>
+ collection.id === action.payload.id ? action.payload : collection
+ )
+ };
+
+ default:
+ return state;
+ }
+}
diff --git a/client/modules/IDE/reducers/collections.js b/client/modules/IDE/reducers/collections.js
index c7017c29a7..0aad556bea 100644
--- a/client/modules/IDE/reducers/collections.js
+++ b/client/modules/IDE/reducers/collections.js
@@ -1,25 +1,60 @@
import * as ActionTypes from '../../../constants';
-const sketches = (state = [], action) => {
+const initialState = {
+ collections: [],
+ metadata: {
+ page: 1,
+ totalPages: 1,
+ totalCollections: 0,
+ limit: 10,
+ hasPagination: false
+ }
+};
+
+const normalizeSetCollections = (data) => {
+ if (data == null) {
+ return initialState;
+ }
+ if (Array.isArray(data)) {
+ return {
+ collections: data,
+ metadata: initialState.metadata
+ };
+ }
+ return {
+ collections: data.collections ?? [],
+ metadata: data.metadata ?? initialState.metadata
+ };
+};
+
+const sketches = (state = initialState, action) => {
switch (action.type) {
case ActionTypes.SET_COLLECTIONS:
- return action.collections;
+ return normalizeSetCollections(action.collections);
case ActionTypes.DELETE_COLLECTION:
- return state.filter(({ id }) => action.collectionId !== id);
+ return {
+ ...state,
+ collections: state.collections.filter(
+ ({ id }) => action.collectionId !== id
+ )
+ };
// The API returns the complete new edited collection
// with any items added or removed
case ActionTypes.EDIT_COLLECTION:
case ActionTypes.ADD_TO_COLLECTION:
case ActionTypes.REMOVE_FROM_COLLECTION:
- return state.map((collection) => {
- if (collection.id === action.payload.id) {
- return action.payload;
- }
+ return {
+ ...state,
+ collections: state.collections.map((collection) => {
+ if (collection.id === action.payload.id) {
+ return action.payload;
+ }
- return collection;
- });
+ return collection;
+ })
+ };
default:
return state;
}
diff --git a/client/modules/IDE/reducers/ide.js b/client/modules/IDE/reducers/ide.js
index 8d45b8b50a..64765cac5b 100644
--- a/client/modules/IDE/reducers/ide.js
+++ b/client/modules/IDE/reducers/ide.js
@@ -22,7 +22,6 @@ const initialState = {
justOpenedProject: false,
previousPath: '/',
errorType: undefined,
- runtimeErrorWarningVisible: false,
parentId: undefined
};
@@ -111,10 +110,6 @@ const ide = (state = initialState, action) => {
return Object.assign({}, state, { errorType: action.modalType });
case ActionTypes.HIDE_ERROR_MODAL:
return Object.assign({}, state, { errorType: undefined });
- case ActionTypes.HIDE_RUNTIME_ERROR_WARNING:
- return Object.assign({}, state, { runtimeErrorWarningVisible: false });
- case ActionTypes.SHOW_RUNTIME_ERROR_WARNING:
- return Object.assign({}, state, { runtimeErrorWarningVisible: true });
case ActionTypes.OPEN_UPLOAD_FILE_MODAL:
return Object.assign({}, state, {
uploadFileModalVisible: true,
diff --git a/client/modules/IDE/selectors/collections.js b/client/modules/IDE/selectors/collections.js
index 02b38305f0..23b865aeaf 100644
--- a/client/modules/IDE/selectors/collections.js
+++ b/client/modules/IDE/selectors/collections.js
@@ -3,7 +3,15 @@ import differenceInMilliseconds from 'date-fns/differenceInMilliseconds';
import { find, orderBy } from 'lodash';
import { DIRECTION } from '../actions/sorting';
-const getCollections = (state) => state.collections;
+/** List API returns `{ collections, metadata }`; this slice may also be a legacy plain array. */
+function collectionsFromSlice(slice) {
+ if (slice == null) return [];
+ if (Array.isArray(slice)) return slice;
+ if (Array.isArray(slice.collections)) return slice.collections;
+ return [];
+}
+
+const getCollections = (state) => collectionsFromSlice(state.collections);
const getField = (state) => state.sorting.field;
const getDirection = (state) => state.sorting.direction;
const getSearchTerm = (state) => state.search.collectionSearchTerm;
diff --git a/client/reducers.ts b/client/reducers.ts
index 8e5ed7b67d..607adcc991 100644
--- a/client/reducers.ts
+++ b/client/reducers.ts
@@ -13,6 +13,7 @@ import search from './modules/IDE/reducers/search';
import sorting from './modules/IDE/reducers/sorting';
import loading from './modules/IDE/reducers/loading';
import collections from './modules/IDE/reducers/collections';
+import collectionsListCollections from './modules/IDE/reducers/CollectionsListCollection';
import collectionsListProjects from './modules/IDE/reducers/collectionsListProjects';
const rootReducer = combineReducers({
@@ -30,6 +31,7 @@ const rootReducer = combineReducers({
assets,
loading,
collections,
+ collectionsListCollections,
collectionsListProjects
});
diff --git a/client/styles/abstracts/_variables.scss b/client/styles/abstracts/_variables.scss
index b1eebc3011..df0189462f 100644
--- a/client/styles/abstracts/_variables.scss
+++ b/client/styles/abstracts/_variables.scss
@@ -6,33 +6,68 @@ $p5js-pink-opacity: #ed225d80;
$p5js-active-pink: #f10046;
$white: #fff;
$black: #000;
-$yellow: #F5DC23;
-$dodgerblue: #1E90FF;
-$p5-contrast-pink: #FFA9D9;
+$yellow: #f5dc23;
+$dodgerblue: #1e90ff;
+$p5-contrast-pink: #ffa9d9;
-$outline-color: #0F9DD7;
+$outline-color: #0f9dd7;
// Grayscale values
-$lightest: #FFF; // primary
-$lighter: #FBFBFB;
+$lightest: #fff; // primary
+$lighter: #fbfbfb;
-$light: #F0F0F0; // primary
-$medium-light: #D9D9D9;
-$middle-light: #A6A6A6;
+$light: #f0f0f0; // primary
+$medium-light: #d9d9d9;
+$middle-light: #a6a6a6;
// $middle-gray: #7D7D7D; // primary
$middle-gray: #747474; // primary
$middle-dark: #666;
-$medium-dark: #4D4D4D;
+$medium-dark: #4d4d4d;
$dark: #333; // primary
-$darker: #1C1C1C;
+$darker: #1c1c1c;
$darkest: #000;
+// Light highlight styles
+$p5-light-selected: $medium-light;
+$p5-light-activeline: #cfcfcf44;
+$p5-light-brown: #7a5a3a;
+$p5-light-black: #333333;
+$p5-light-pink: #d52889;
+$p5-light-gray: #666;
+$p5-light-blue: #0b7ca9;
+$p5-light-orange: #a06801;
+$p5-light-lightgray: $middle-gray;
+$p5-light-green: #47820a;
+
+// Dark highlight styles
+$p5-dark-selected: $medium-dark;
+$p5-dark-activeline: #cfcfcf22;
+$p5-dark-pink: #de4a9b;
+$p5-dark-gray: #9b9b9b;
+$p5-dark-lightblue: #0f9dd7;
+$p5-dark-white: #fdfdfd;
+$p5-dark-orange: #ee9900;
+$p5-dark-green: #58a10b;
+$p5-dark-goldbrown: #b58318;
+
+// Contrast highlight styles
+$p5-contrast-selected: $middle-dark;
+$p5-contrast-activeline: #333333aa;
+$p5-contrast-white: #fdfdfd;
+$p5-contrast-lightgray: #c1c1c1;
+$p5-contrast-blue: #00ffff;
+$p5-contrast-green: #2de9b6;
+$p5-contrast-yellow: #f5dc23;
+$p5-contrast-orange: #ffa95d;
+$p5-contrast-pink: #ffa9d9;
+
$themes: (
light: (
- admonition-background: #E4F8FF,
- admonition-border: #22C8ED,
+ activeline-background-color: $p5-light-activeline,
+ admonition-background: #e4f8ff,
+ admonition-border: #22c8ed,
admonition-text: #075769,
background-color: $lighter,
button-active-color: $lightest,
@@ -44,9 +79,9 @@ $themes: (
button-hover-color: $lightest,
button-nav-inactive-color: $middle-gray,
button-secondary-background-color: $medium-light,
- codefold-icon-closed: url("../images/triangle-arrow-right.svg?byUrl"),
- codefold-icon-open: url("../images/triangle-arrow-down.svg?byUrl"),
- console-active-arrow-color: #0071AD,
+ codefold-icon-closed: url('../images/triangle-arrow-right.svg?byUrl'),
+ codefold-icon-open: url('../images/triangle-arrow-down.svg?byUrl'),
+ console-active-arrow-color: #0071ad,
console-arrow-color: $middle-gray,
console-background-color: $light,
console-color: $darker,
@@ -63,13 +98,26 @@ $themes: (
form-secondary-title-color: $medium-dark,
form-title-color: rgba(51, 51, 51, 0.87),
heavy-text-color: $darker,
+ highlight-style-comment-color: $p5-light-lightgray,
+ highlight-style-variable-color: $p5-light-blue,
+ highlight-style-string-color: $p5-light-green,
+ highlight-style-regexp-color: $p5-light-orange,
+ highlight-style-number-color: $p5-light-black,
+ highlight-style-atom-color: $p5-light-pink,
+ highlight-style-keyword-color: $p5-light-brown,
+ highlight-style-operator-color: $p5-light-brown,
+ highlight-style-def-color: $p5-light-blue,
+ highlight-style-tag-color: $p5-light-pink,
+ highlight-style-property-color: $p5-light-black,
+ highlight-style-attribute-color: $p5-light-black,
+ highlight-style-matching-selection-background-color: $p5js-pink-opacity,
hint-arrow-background-active-color: $p5js-active-pink,
hint-arrow-background-color: #ed225ddd,
hint-arrow-color: $lightest,
hint-arrow-focus-outline-color: $middle-dark,
hint-background-color: $white,
- hint-fun-active-border-bottom-color: #0B7CA9,
- hint-fun-text-color: #0B7CA9,
+ hint-fun-active-border-bottom-color: #0b7ca9,
+ hint-fun-text-color: #0b7ca9,
hint-inline-text-color-light: $middle-light,
hint-inline-text-color: $middle-gray,
hint-item-active-background-color: $middle-gray,
@@ -79,12 +127,12 @@ $themes: (
hint-item-active-type-text-color: $white,
hint-item-border-bottom-color: $white,
hint-item-hover-background-color: #f4f4f4,
- hint-keyword-text-color: #7A5A3A,
+ hint-keyword-text-color: #7a5a3a,
hint-no-link-background-color: $medium-light,
hint-text-color: $dark,
hint-type-text-color: $medium-dark,
- hint-var-active-border-bottom-color: #D52889,
- hint-var-text-color: #D52889,
+ hint-var-active-border-bottom-color: #d52889,
+ hint-var-text-color: #d52889,
icon-color: $middle-gray,
icon-hover-color: $darker,
icon-toast-hover-color: $lightest,
@@ -114,9 +162,10 @@ $themes: (
search-hover-background-color: $medium-dark,
search-hover-text-color: $lightest,
secondary-text-color: $medium-dark,
+ selected-background-color: $p5-light-selected,
shadow-color: rgba(0, 0, 0, 0.16),
table-button-active-color: $lightest,
- table-button-background-active-color: #00A1D3,
+ table-button-background-active-color: #00a1d3,
table-button-background-color: $middle-gray,
table-button-background-hover-color: $p5js-pink,
table-button-color: $lightest,
@@ -126,12 +175,13 @@ $themes: (
toast-background-color: $medium-dark,
toast-text-color: $lightest,
toolbar-button-background-color: $medium-light,
- toolbar-button-color: $dark,
+ toolbar-button-color: $dark
),
dark: (
- admonition-background: #105A7F,
- admonition-border: #22C8ED,
- admonition-text: #FFFFFF,
+ activeline-background-color: $p5-dark-activeline,
+ admonition-background: #105a7f,
+ admonition-border: #22c8ed,
+ admonition-text: #ffffff,
background-color: $darker,
button-active-color: $lightest,
button-background-active-color: $p5js-active-pink,
@@ -142,9 +192,9 @@ $themes: (
button-hover-color: $lightest,
button-nav-inactive-color: $middle-light,
button-secondary-background-color: $medium-dark,
- codefold-icon-closed: url("../images/triangle-arrow-right-white.svg?byUrl"),
- codefold-icon-open: url("../images/triangle-arrow-down-white.svg?byUrl"),
- console-active-arrow-color: #097BB3,
+ codefold-icon-closed: url('../images/triangle-arrow-right-white.svg?byUrl'),
+ codefold-icon-open: url('../images/triangle-arrow-down-white.svg?byUrl'),
+ console-active-arrow-color: #097bb3,
console-arrow-color: $medium-light,
console-background-color: $dark,
console-color: $lightest,
@@ -160,13 +210,26 @@ $themes: (
form-secondary-title-color: $medium-light,
form-title-color: $lightest,
heavy-text-color: $lightest,
+ highlight-style-comment-color: $p5-dark-gray,
+ highlight-style-variable-color: $p5-dark-lightblue,
+ highlight-style-string-color: $p5-dark-green,
+ highlight-style-regexp-color: $p5-dark-orange,
+ highlight-style-number-color: $p5-dark-white,
+ highlight-style-atom-color: $p5-dark-pink,
+ highlight-style-keyword-color: $p5-dark-goldbrown,
+ highlight-style-operator-color: $p5-dark-goldbrown,
+ highlight-style-def-color: $p5-dark-lightblue,
+ highlight-style-tag-color: $p5-dark-pink,
+ highlight-style-property-color: $p5-dark-white,
+ highlight-style-attribute-color: $p5-dark-lightblue,
+ highlight-style-matching-selection-background-color: $p5js-pink-opacity,
hint-arrow-background-active-color: $p5js-active-pink,
hint-arrow-background-color: #ed225ddd,
hint-arrow-color: $lightest,
hint-arrow-focus-outline-color: #cfcfcf,
hint-background-color: $darker,
- hint-fun-active-border-bottom-color: #0F9DD7,
- hint-fun-text-color: #0F9DD7,
+ hint-fun-active-border-bottom-color: #0f9dd7,
+ hint-fun-text-color: #0f9dd7,
hint-inline-text-color-light: $middle-gray,
hint-inline-text-color: #cfcfcf,
hint-item-active-background-color: #cfcfcf,
@@ -176,12 +239,12 @@ $themes: (
hint-item-active-type-text-color: $darker,
hint-item-border-bottom-color: $darker,
hint-item-hover-background-color: $medium-dark,
- hint-keyword-text-color: #B58318,
+ hint-keyword-text-color: #b58318,
hint-no-link-background-color: $medium-dark,
hint-text-color: $light,
hint-type-text-color: $light,
- hint-var-active-border-bottom-color: #DE4A9B,
- hint-var-text-color: #DE4A9B,
+ hint-var-active-border-bottom-color: #de4a9b,
+ hint-var-text-color: #de4a9b,
icon-color: $middle-light,
icon-hover-color: $lightest,
icon-toast-hover-color: $lightest,
@@ -211,9 +274,10 @@ $themes: (
search-hover-background-color: $p5js-pink,
search-hover-text-color: $lightest,
secondary-text-color: $medium-light,
+ selected-background-color: $p5-dark-selected,
shadow-color: rgba(0, 0, 0, 0.16),
table-button-active-color: $lightest,
- table-button-background-active-color: #00A1D3,
+ table-button-background-active-color: #00a1d3,
table-button-background-color: $middle-gray,
table-button-background-hover-color: $p5js-pink,
table-button-color: $lightest,
@@ -223,11 +287,12 @@ $themes: (
toast-background-color: $medium-light,
toast-text-color: $dark,
toolbar-button-background-color: $medium-dark,
- toolbar-button-color: $lightest,
+ toolbar-button-color: $lightest
),
contrast: (
+ activeline-background-color: $p5-contrast-activeline,
admonition-background: #000000,
- admonition-border: #22C8ED,
+ admonition-border: #22c8ed,
admonition-text: #ffffff,
background-color: $darker,
button-active-color: $dark,
@@ -239,8 +304,8 @@ $themes: (
button-hover-color: $dark,
button-nav-inactive-color: $light,
button-secondary-background-color: $medium-dark,
- codefold-icon-closed: url("../images/triangle-arrow-right-white.svg?byUrl"),
- codefold-icon-open: url("../images/triangle-arrow-down-white.svg?byUrl"),
+ codefold-icon-closed: url('../images/triangle-arrow-right-white.svg?byUrl'),
+ codefold-icon-open: url('../images/triangle-arrow-down-white.svg?byUrl'),
console-active-arrow-color: $dodgerblue,
console-arrow-color: $lightest,
console-background-color: $dark,
@@ -256,13 +321,26 @@ $themes: (
form-secondary-title-color: $medium-light,
form-title-color: $lightest,
heavy-text-color: $yellow,
- hint-arrow-background-active-color: #F5DC23,
- hint-arrow-background-color: #F5DC23DD,
+ highlight-style-comment-color: $p5-contrast-lightgray,
+ highlight-style-variable-color: $p5-contrast-blue,
+ highlight-style-string-color: $p5-contrast-green,
+ highlight-style-regexp-color: $p5-contrast-green,
+ highlight-style-number-color: $p5-contrast-pink,
+ highlight-style-atom-color: $p5-contrast-pink,
+ highlight-style-keyword-color: $p5-contrast-yellow,
+ highlight-style-operator-color: $p5-contrast-lightgray,
+ highlight-style-def-color: $p5-contrast-blue,
+ highlight-style-tag-color: $p5-contrast-orange,
+ highlight-style-property-color: $p5-contrast-white,
+ highlight-style-attribute-color: $p5-contrast-white,
+ highlight-style-matching-selection-background-color: $medium-dark,
+ hint-arrow-background-active-color: #f5dc23,
+ hint-arrow-background-color: #f5dc23dd,
hint-arrow-color: $darker,
hint-arrow-focus-outline-color: $lighter,
hint-background-color: $darkest,
hint-fun-active-border-bottom-color: none,
- hint-fun-text-color: #00FFFF,
+ hint-fun-text-color: #00ffff,
hint-inline-text-color-light: $middle-gray,
hint-inline-text-color: #cfcfcf,
hint-item-active-background-color: unset,
@@ -272,12 +350,12 @@ $themes: (
hint-item-active-type-text-color: $lighter,
hint-item-border-bottom-color: $medium-dark,
hint-item-hover-background-color: $dark,
- hint-keyword-text-color: #F5DC23,
+ hint-keyword-text-color: #f5dc23,
hint-no-link-background-color: $medium-dark,
hint-text-color: $medium-light,
hint-type-text-color: $middle-light,
hint-var-active-border-bottom-color: none,
- hint-var-text-color: #FFA9D9,
+ hint-var-text-color: #ffa9d9,
icon-color: $medium-light,
icon-hover-color: $yellow,
icon-toast-hover-color: $yellow,
@@ -307,9 +385,10 @@ $themes: (
search-hover-background-color: $yellow,
search-hover-text-color: $dark,
secondary-text-color: $lighter,
+ selected-background-color: $p5-contrast-selected,
shadow-color: rgba(0, 0, 0, 0.16),
table-button-active-color: $dark,
- table-button-background-active-color: #00FFFF,
+ table-button-background-active-color: #00ffff,
table-button-background-color: $middle-gray,
table-button-background-hover-color: $yellow,
table-button-color: $dark,
@@ -319,6 +398,6 @@ $themes: (
toast-background-color: $medium-light,
toast-text-color: $darker,
toolbar-button-background-color: $medium-light,
- toolbar-button-color: $dark,
+ toolbar-button-color: $dark
)
);
diff --git a/client/styles/components/_collection.scss b/client/styles/components/_collection.scss
index bc5f9db5db..412553e93d 100644
--- a/client/styles/components/_collection.scss
+++ b/client/styles/components/_collection.scss
@@ -183,3 +183,44 @@
}
}
}
+
+.pagination {
+ width: 100%;
+ position: absolute;
+ z-index: 1;
+ bottom: 1.5rem;
+ left: 0;
+
+ .pagination-ul {
+ bottom: 0;
+ display: flex;
+ justify-content: center;
+
+ > li {
+ margin: 0 0.2rem;
+
+ .page-link {
+ font-weight: 800;
+ padding: 0 1rem;
+ }
+ }
+
+ .bold-text {
+ font-weight: bold;
+ }
+ }
+
+ .page-link:disabled,
+ .page-item.disabled .page-link {
+ cursor: not-allowed;
+ opacity: 0.5;
+ }
+}
+
+.pagination-overlay {
+ bottom: 1rem;
+}
+
+.pagination-ul > li button:hover {
+ text-decoration: underline;
+}
diff --git a/client/styles/components/_editor.scss b/client/styles/components/_editor.scss
index 0aab1d4b9f..93866036cd 100644
--- a/client/styles/components/_editor.scss
+++ b/client/styles/components/_editor.scss
@@ -1,386 +1,168 @@
@use "sass:math";
-.CodeMirror {
+.cm-editor {
font-family: Inconsolata, monospace;
height: 100%;
-}
-
-.CodeMirror-linenumbers {
- padding-right: #{math.div(10, $base-font-size)}rem;
-}
-.CodeMirror-linenumber {
- width: #{math.div(32, $base-font-size)}rem;
- left: #{math.div(-3, $base-font-size)}rem !important;
@include themify() {
- color: getThemifyVariable("inactive-text-color");
+ color: getThemifyVariable('primary-text-color');
}
-}
-
-.CodeMirror-lines {
- padding-top: #{math.div(25, $base-font-size)}rem;
-}
-
-pre.CodeMirror-line {
- padding-left: #{math.div(5, $base-font-size)}rem;
-}
-
-.CodeMirror-gutter-wrapper {
- right: 100%;
- top: 0;
- bottom: 0;
-}
-
-.CodeMirror-lint-marker-warning,
-.CodeMirror-lint-marker-error,
-.CodeMirror-lint-marker-multiple {
- background-image: none;
- width: #{math.div(49, $base-font-size)}rem;
- position: absolute;
- height: 100%;
- right: 100%;
-}
-
-.CodeMirror-lint-message-error,
-.CodeMirror-lint-message-warning {
- background-image: none;
- padding-left: inherit;
-}
-
-.CodeMirror-lint-marker-warning {
- background-color: rgb(255, 190, 5);
-}
-.CodeMirror-lint-marker-error {
- background-color: rgb(255, 95, 82);
-}
+ .cm-content {
+ padding-top: #{math.div(25, $base-font-size)}rem;
+
+ .cm-activeLine {
+ // Needed to place the active line highlight behind the selection highlight.
+ position: relative;
+ background-color: transparent;
+
+ &::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: -10;
+
+ @include themify() {
+ background-color: getThemifyVariable('activeline-background-color');
+ }
+ }
+ }
-.CodeMirror-gutter-elt:not(.CodeMirror-linenumber) {
- opacity: 0.2;
- width: #{math.div(49, $base-font-size)}rem !important;
- height: 100%;
- left: 49px !important;
- // background-color: rgb(255, 95, 82);
-}
+ .cm-errorLine.cm-activeLine,
+ .cm-errorLine {
+ background-color: rgb(255 95 82 / 30%);
+ }
+ }
+ .cm-focused > .cm-scroller .cm-selectionLayer .cm-selectionBackground,
+ .cm-selectionLayer .cm-selectionBackground,
+ ::selection {
+ @include themify() {
+ background-color: getThemifyVariable(
+ 'selected-background-color'
+ ) !important;
+ }
+ }
-.CodeMirror-lint-tooltip {
- @include themify() {
- background-color: getThemifyVariable("modal-background-color");
- border: 1px solid getThemifyVariable("modal-border-color");
- box-shadow: 0 12px 12px getThemifyVariable("shadow-color");
- color: getThemifyVariable("primary-text-color");
+ .cm-cursorLayer .cm-cursor {
+ @include themify() {
+ border-left-color: getThemifyVariable('primary-text-color');
+ }
}
- border-radius: 2px;
- font-family: Montserrat, sans-serif;
}
-.CodeMirror-gutters {
+.cm-gutters {
@include themify() {
- background-color: getThemifyVariable("editor-gutter-color");
- border-color: getThemifyVariable("ide-border-color");
+ background-color: getThemifyVariable('editor-gutter-color');
+ border-color: getThemifyVariable('ide-border-color');
}
- // left: 0 !important;
width: #{math.div(48, $base-font-size)}rem;
-}
-
-/*
- Search dialog
-*/
+ position: relative;
-.CodeMirror-dialog {
- position: fixed;
- top: 0;
- left: 50%;
- margin-left: #{math.div(-552 * 0.5, $base-font-size)}rem;
-
- @media (max-width: 770px) {
- left: 0;
- right: 0;
- width: 100%;
- margin-left: 0;
+ .cm-gutter {
+ z-index: 1;
}
- z-index: 10;
-
- width: 580px;
- font-family: Montserrat, sans-serif;
-
- padding: #{math.div(8, $base-font-size)}rem #{math.div(10, $base-font-size)}rem #{math.div(5, $base-font-size)}rem #{math.div(9, $base-font-size)}rem;
-
- border-radius: 2px;
-
- @include themify() {
- background-color: getThemifyVariable("modal-background-color");
- box-shadow: 0 12px 12px 0 getThemifyVariable("shadow-color");
- border: solid 0.5px getThemifyVariable("modal-border-color");
+ .cm-lineNumbers {
+ @include themify() {
+ color: getThemifyVariable('inactive-text-color');
+ }
+ pointer-events: none;
}
-}
-.CodeMirror-find-popup-container {
- display: flex;
- flex-wrap: wrap;
- justify-content: space-between;
-}
-
-.Toggle-replace-btn-div {
- height: #{math.div(40, $base-font-size)}rem;
- padding: 0;
-}
-
-.Toggle-replace-btn-div > button {
- width: 100%;
- height: 100%;
-}
-
-.CodeMirror-search-results {
- margin: 0 #{math.div(20, $base-font-size)}rem;
- width: #{math.div(75, $base-font-size)}rem;
- font-size: #{math.div(12, $base-font-size)}rem;
-}
-
-.CodeMirror-find-controls {
- width: 100%;
- display: flex;
- align-items: center;
- justify-content: space-between;
- height: #{math.div(35, $base-font-size)}rem;
-}
-.CodeMirror-search-inputs {
- width: 30%;
- margin-left: 10px;
-}
-.CodeMirror-replace-div {
- display: flex;
- justify-content: flex-start;
- align-items: center;
-}
-.CodeMirror-search-controls {
- width: 60%;
- display: flex;
- flex-wrap: wrap-reverse;
- justify-content: flex-start;
- align-items: flex-end;
-}
-.CodeMirror-replace-controls {
- display: flex;
- margin-left: #{math.div(10, $base-font-size)}rem;
-}
-
-.CodeMirror-replace-options {
- width: #{math.div(552, $base-font-size)}rem;
- height: #{math.div(65, $base-font-size)}rem;
- display: flex;
- justify-content: center;
- align-items: center;
-}
-.CodeMirror-replace-options button {
- width: #{math.div(200, $base-font-size)}rem;
-}
+ .cm-activeLineGutter {
+ background-color: transparent;
+ }
-.CodeMirror-search-title {
- display: block;
- margin-bottom: #{math.div(12, $base-font-size)}rem;
+ .cm-gutter-lint {
+ position: absolute;
+ left: 0;
+ width: 100%;
+ z-index: 0;
- font-size: #{math.div(21, $base-font-size)}rem;
- font-weight: bold;
-}
+ .cm-activeLineGutter {
+ @include themify() {
+ background-color: getThemifyVariable('active-line-background-color');
+ }
+ }
-.CodeMirror-search-field {
- display: block;
- width: 100%;
- max-width: #{math.div(166, $base-font-size)}rem;
- margin-bottom: #{math.div(4, $base-font-size)}rem;
- @include themify() {
- color: getThemifyVariable("input-text-color");
- background-color: getThemifyVariable("input-secondary-background-color");
- border: solid 0.5px getThemifyVariable("button-border-color");
- &::placeholder {
- color: getThemifyVariable("inactive-text-color");
+ .cm-gutterElement {
+ padding: 0;
}
}
-}
-
-.CodeMirror-search-nav {
- display: flex;
- align-items: center;
-}
-
-.CodeMirror-search-count {
- display: block;
- height: #{math.div(20, $base-font-size)}rem;
- text-align: right;
-}
-
-.CodeMirror-search-actions {
- display: flex;
- justify-content: space-between;
-}
-
-.CodeMirror-search-modifiers {
- display: flex;
- justify-content: flex-end;
- align-items: center;
- margin-left: #{math.div(10, $base-font-size)}rem;
- @media (max-width: 579px) {
- display: none;
- }
-}
+ .cm-lint-marker {
+ content: '';
+ width: 100%;
+ height: 100%;
+ opacity: 0.2;
-.CodeMirror-regexp-button,
-.CodeMirror-case-button,
-.CodeMirror-word-button {
- @include themify() {
- // @extend %button;
- padding: #{math.div(2, $base-font-size)}rem #{math.div(7, $base-font-size)}rem;
- border: 2px solid transparent;
- &:hover {
- border-color: getThemifyVariable("button-border-color");
+ &.cm-lint-marker-error {
+ background-color: rgb(255, 95, 82);
}
- }
- width: #{math.div(35, $base-font-size)}rem;
- height: #{math.div(35, $base-font-size)}rem;
- & + & {
- margin-left: #{math.div(3, $base-font-size)}rem;
+ &.cm-lint-marker-warning {
+ background-color: rgb(255, 190, 5);
+ }
}
-
- word-break: keep-all;
- white-space: nowrap;
-}
-
-.CodeMirror-regexp-button .label,
-.CodeMirror-case-button .label,
-.CodeMirror-word-button .label {
- @extend %hidden-element;
}
-[aria-checked="true"] {
+.cm-tooltip-lint {
@include themify() {
- color: getThemifyVariable("heavy-text-color");
- background-color: getThemifyVariable("button-secondary-background-color");
- border-color: getThemifyVariable("button-border-color");
+ background-color: getThemifyVariable('modal-background-color');
+ border: 1px solid getThemifyVariable('modal-border-color');
+ box-shadow: 0 12px 12px getThemifyVariable('shadow-color');
+ color: getThemifyVariable('primary-text-color');
}
+ border-radius: 2px;
+ font-family: Montserrat, sans-serif;
}
-/*
- Previous / Next buttons
-*/
-
-// Visually hide button text
-.CodeMirror-search-button .label {
- @extend %hidden-element;
-}
-
-.CodeMirror-search-button {
- margin-right: #{math.div(10, $base-font-size)}rem;
-}
-
-.CodeMirror-search-match {
- background: gold;
- border-top: #{math.div(1, $base-font-size)}rem solid orange;
- border-bottom: #{math.div(1, $base-font-size)}rem solid orange;
- box-sizing: border-box;
- opacity: 0.5;
-}
-
-/*
- Close button
-*/
-.CodeMirror-close-button-container {
- display: flex;
- align-items: center;
-}
-
-// foldgutter
-.CodeMirror-foldmarker {
- text-shadow:
- -1px 0 #ed225d,
- 0 1px #ed225d,
- 1px 0 #ed225d,
- 0 -1px #ed225d;
- color: #fff;
- /* background-color: rgba(237, 34, 93, 0.42); */
- /* border-radius: 3px; */
- font-weight: bold;
- font-family: arial;
- line-height: 0.3;
- cursor: pointer;
- opacity: 0.75;
-}
-.CodeMirror-foldgutter {
- width: 2.7em;
-}
-.CodeMirror-foldgutter-open,
-.CodeMirror-foldgutter-folded {
- cursor: pointer;
- padding-bottom: 0.4em;
- text-align: right;
- line-height: 1;
-}
-.CodeMirror-foldgutter-open:after {
- content: "\25BE";
-}
-.CodeMirror-foldgutter-folded:after {
- content: "\25B8";
-}
-
-.CodeMirror-foldgutter-open,
-.CodeMirror-foldgutter-folded {
- position: absolute;
- right: 100%;
-}
-
-.CodeMirror-foldgutter-open:after {
+/** Search dialog */
+.cm-search.cm-panel {
@include themify() {
- background-image: getThemifyVariable("codefold-icon-open");
+ color: getThemifyVariable('primary-text-color');
+ background-color: getThemifyVariable('modal-background-color');
+ box-shadow: 0 12px 12px 0 getThemifyVariable('shadow-color');
+ border: solid 0.5px getThemifyVariable('modal-border-color');
}
-}
+ font-family: Montserrat, sans-serif;
-.CodeMirror-foldgutter-folded:after {
- @include themify() {
- background-image: getThemifyVariable("codefold-icon-closed");
+ .cm-button {
+ @include themify() {
+ @extend %link;
+ color: getThemifyVariable('secondary-text-color');
+ &:hover {
+ color: getThemifyVariable('logo-color');
+ }
+ }
+ background: transparent;
+ border: none;
+ font-size: 1rem;
}
-}
-
-.CodeMirror-foldgutter-folded:after,
-.CodeMirror-foldgutter-open:after {
- background-size: 10px 10px;
- content: "";
- padding-left: 15px;
- background-repeat: no-repeat;
- background-position: center center;
-}
-.CodeMirror-foldmarker {
- text-shadow: none;
- border-radius: 5px;
- opacity: 1;
- font-weight: normal;
- display: inline-block;
- vertical-align: middle;
- height: 0.85em;
- line-height: 0.7;
- padding: 0 #{math.div(5, $base-font-size)}rem;
- font-family: serif;
-}
-
-.line-runtime-error + .CodeMirror-activeline-gutter {
- background-color: rgb(255, 95, 82);
- opacity: 0.3;
+ input.cm-textfield {
+ @include themify() {
+ color: getThemifyVariable('input-text-color');
+ background-color: getThemifyVariable('input-secondary-background-color');
+ border: solid 0.5px getThemifyVariable('button-border-color');
+ &::placeholder {
+ color: getThemifyVariable('inactive-text-color');
+ }
+ }
+ }
}
-.line-runtime-error {
- background-color: rgb(255, 95, 82) !important;
- opacity: 0.3;
-}
+// TODO: Add fold gutter styling back.
.editor-holder {
height: calc(100% - #{math.div(29, $base-font-size)}rem);
width: 100%;
position: absolute;
@include themify() {
- border: 1px solid getThemifyVariable("ide-border-color");
+ border: 1px solid getThemifyVariable('ide-border-color');
}
&.editor-holder--hidden .CodeMirror {
display: none;
@@ -393,7 +175,7 @@ pre.CodeMirror-line {
.editor__file-name {
@include themify() {
- color: getThemifyVariable("primary-text-color");
+ color: getThemifyVariable('primary-text-color');
}
height: #{math.div(29, $base-font-size)}rem;
padding-top: #{math.div(7, $base-font-size)}rem;
@@ -406,7 +188,7 @@ pre.CodeMirror-line {
.editor__library-version {
@include themify() {
- color: getThemifyVariable("primary-text-color");
+ color: getThemifyVariable('primary-text-color');
}
position: absolute;
top: 0;
@@ -422,41 +204,125 @@ pre.CodeMirror-line {
}
/** Inline abbreviation preview */
-
-.emmet-abbreviation-preview {
+.emmet-preview.cm-tooltip {
@extend %modal;
- position: absolute;
@include themify() {
- background: getThemifyVariable("background-color");
+ background: getThemifyVariable('background-color');
}
- & .CodeMirror-lines {
+ .cm-content {
padding: 0;
}
- & .CodeMirror {
- height: auto;
- max-width: #{math.div(400, $base-font-size)}rem;
- max-height: #{math.div(300, $base-font-size)}rem;
- border: none;
+}
+
+// Used for comment, lineComment, blockComment, docComment, docString
+.cm-comment {
+ @include themify() {
+ color: getThemifyVariable('highlight-style-comment-color');
+ }
+}
+
+// Used for name, variableName, typeName
+.cm-variable {
+ @include themify() {
+ color: getThemifyVariable('highlight-style-variable-color');
+ }
+}
+
+// Used for string, character, attributeValue
+.cm-string {
+ @include themify() {
+ color: getThemifyVariable('highlight-style-string-color');
+ }
+}
+
+// Used for regexp
+.cm-regexp {
+ @include themify() {
+ color: getThemifyVariable('highlight-style-regexp-color');
+ }
+}
+
+// Used for number, integer, float
+.cm-number {
+ @include themify() {
+ color: getThemifyVariable('highlight-style-number-color');
+ }
+}
+
+// Used for bool, atom, null
+.cm-atom {
+ @include themify() {
+ color: getThemifyVariable('highlight-style-atom-color');
}
}
-.emmet-abbreviation-preview:not(.has-error) .emmet-abbreviation-preview-error {
- display: none;
+// Used for keyword, self, function, className
+.cm-keyword {
+ @include themify() {
+ color: getThemifyVariable('highlight-style-keyword-color');
+ }
}
-.emmet-abbreviation-preview.has-error .CodeMirror {
- display: none;
+// Used for operatorKeyword, controlKeyword, operator, derefOperator, arithmeticOperator,
+// logicOperator, bitwiseOperator, compareOperator, updateOperator, typeOperator
+// controlOperator
+.cm-operator {
+ @include themify() {
+ color: getThemifyVariable('highlight-style-operator-color');
+ }
}
-.emmet-abbreviation-preview .CodeMirror-cursors {
- visibility: hidden !important;
+// Used for definitionKeyword, definition, const, local
+.cm-def {
+ @include themify() {
+ color: getThemifyVariable('highlight-style-def-color');
+ }
}
-.emmet-abbreviation-preview .emmet-error-snippet-message {
- padding: 5px;
+// tagName, heading, heading1, heading2, heading3, heading4, heading5, heading6,
+// list, quote, emphasis, strong, link
+.cm-tag {
+ @include themify() {
+ color: getThemifyVariable('highlight-style-tag-color');
+ }
}
-.emmet-open-tag,
-.emmet-close-tag {
- text-decoration: underline;
+// Used for propertyName
+.cm-property {
+ @include themify() {
+ color: getThemifyVariable('highlight-style-property-color');
+ }
}
+
+// Used for attributeName
+.cm-attribute {
+ @include themify() {
+ color: getThemifyVariable('highlight-style-attribute-color');
+ }
+}
+
+// Unassigned Lezer tags: literal, escape, color, url, unit, modifier, punctuation,
+// separator, bracket, angleBracket, squareBracket, paren, brace, content
+// contentSeparator, monospace, strikethrough, inserted, deleted, changed, invalid, meta,
+// documentMeta, annotation, processingInstruction, standard, special, macroName
+
+// TODO(connie): Add p5 specific highlighting styles, like .cm-p5-function, .cm-p5-variable
+
+// Additional matching selection styling
+.cm-matchingBracket {
+ @include themify() {
+ outline: 1px solid getThemifyVariable('primary-text-color');
+ outline-offset: 1px;
+ background-color: transparent !important;
+ }
+}
+
+.cm-selectionMatch,
+.cm-searchMatch,
+.cm-searchMatch-selected {
+ @include themify() {
+ background-color: getThemifyVariable(
+ 'highlight-style-matching-selection-background-color'
+ );
+ }
+}
\ No newline at end of file
diff --git a/client/styles/components/_hints.scss b/client/styles/components/_hints.scss
index 7084b460e7..2c22413ed0 100644
--- a/client/styles/components/_hints.scss
+++ b/client/styles/components/_hints.scss
@@ -1,276 +1,250 @@
@use "sass:math";
-.CodeMirror-hints {
- position: absolute;
- z-index: 10;
- overflow: hidden;
- list-style: none;
-
- margin: 0;
- padding: 0;
-
+.cm-tooltip-autocomplete.CodeMirror-hints {
box-shadow: 0 0 #{math.div(18, $base-font-size)}rem 0 rgba(0, 0, 0, 0.16);
border: #{math.div(1, $base-font-size)}rem solid #A6A6A6;
-
- font-size: 100%;
font-family: Inconsolata, monospace;
-
- width: 20rem;
- max-height: 20rem;
- overflow-y: auto;
-
- transform-origin: top left;
+ font-size: 1rem;
@include themify() {
background: getThemifyVariable('hint-background-color');
+ color: getThemifyVariable('hint-text-color');
+ }
- .CodeMirror-hint {
- color: getThemifyVariable('hint-text-color');
- border-bottom: #{math.div(1, $base-font-size)}rem solid getThemifyVariable('hint-item-border-bottom-color');
+ ul {
+ @include themify() {
+ background: getThemifyVariable('hint-background-color');
}
+ }
- .hint-container {
- display: flex;
- align-items: center;
- flex-direction: column;
- justify-content: center;
- width: 100%;
- height: 100%;
+ li.CodeMirror-hint {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) auto #{math.div(40, $base-font-size)}rem;
+ align-items: center;
+ min-height: #{math.div(24, $base-font-size)}rem;
+ font-weight: 500;
- // Widen the entire row only if a warning is present
- &.has-warning {
- width: 100%; // Let it fill the parent .CodeMirror-hint width
- max-width: 24rem;
- }
+ @include themify() {
+ background: getThemifyVariable('hint-background-color');
+ color: getThemifyVariable('hint-text-color');
+ border-bottom: #{math.div(1, $base-font-size)}rem solid
+ getThemifyVariable('hint-item-border-bottom-color');
}
- .hint-main {
- display: flex;
- justify-content: space-between;
- align-items: center;
- flex-grow: 1;
- padding: 0 0.5rem;
- width: 100%;
- height: 100%;
- // position: relative; // optional, only if you want absolutely-positioned children
+ &:hover:not([aria-selected='true']) {
+ @include themify() {
+ background: getThemifyVariable('hint-item-hover-background-color');
+ }
}
- .hint-main a {
- display: flex;
- align-items: center;
- justify-content: center;
- width: 2rem;
- height: 100%;
- padding: 0;
- // margin-left: auto;
- text-align: center;
- text-decoration: none;
- font-size: 1.2rem;
- }
+ &[aria-selected='true'] {
+ @include themify() {
+ background: getThemifyVariable('hint-item-active-background-color');
+ color: getThemifyVariable('hint-item-active-text-color');
+ outline: getThemifyVariable('hint-item-active-outline');
+ outline-offset: getThemifyVariable('hint-item-active-outline-offset');
+ }
- .hint-name {
- font-size: 1.2rem;
- font-weight: bold;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
+ .cm-completionKind {
+ @include themify() {
+ color: getThemifyVariable('hint-item-active-type-text-color');
+ }
+ }
- .hint-type {
- font-size: 1rem;
- font-weight: normal;
- color: #999;
- margin-left: 1rem;
- margin-right: 2.5rem; // leaves space for the arrow icon
- white-space: nowrap;
- flex-shrink: 0;
+ .cm-completionMatchedText {
+ color: inherit;
+ }
}
- // Warning box
- .blacklist-warning {
- background-color: #fff3cd;
- border: 1px solid #ffc107;
- color: #856404;
- padding: 6px 10px;
- border-radius: 4px;
- font-size: 0.85rem;
- margin: 0.25rem 0.5rem 0 0.5rem;
- width: calc(100% - 1rem); // Match padding
- box-sizing: border-box;
- }
-
- .fun-name, .obj-name {
- color: getThemifyVariable('hint-fun-text-color');
- }
-
- .var-name, .boolean-name {
- color: getThemifyVariable('hint-var-text-color');
+ &.blacklisted {
+ height: auto;
+ min-height: 3.2rem; // enough to show the warning + content
}
+ }
- .keyword-name {
- color: getThemifyVariable('hint-keyword-text-color');
- }
-
- .hint-type {
+ .cm-completionLabel {
+ grid-column: 1;
+ justify-self: start;
+ margin-left: #{math.div(12, $base-font-size)}rem;
+ line-height: 1.1;
+ font-weight: 600;
+ color: inherit;
+ background: transparent;
+ }
+
+ .cm-completionMatchedText {
+ color: inherit;
+ text-decoration: none;
+ font-weight: inherit;
+ }
+
+ .cm-completionDetail {
+ display: none;
+ }
+
+ .cm-completionKind {
+ grid-column: 2;
+ justify-self: end;
+ padding-right: #{math.div(28, $base-font-size)}rem;
+ font-size: 0.8rem;
+
+ @include themify() {
color: getThemifyVariable('hint-type-text-color');
- margin-right: #{math.div(10, $base-font-size)}rem;
}
-
- a {
- color: getThemifyVariable('hint-arrow-color');
+ }
+
+ .cm-completionRefLink {
+ grid-column: 3;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ align-self: stretch;
+ width: 75%;
+ text-decoration: none;
+
+ @include themify() {
background: getThemifyVariable('hint-arrow-background-color');
+ color: getThemifyVariable('hint-arrow-color');
+ }
- &:hover, &:active, &.focused-hint-link {
+ &:hover,
+ &:active,
+ &.focused-hint-link {
+ @include themify() {
background: getThemifyVariable('hint-arrow-background-active-color');
}
+ }
+
+ &.focused-hint-link {
+ width: 100%;
+ height: 100%;
+ margin: 0;
+ align-self: stretch;
- &.focused-hint-link {
- outline: #{math.div(3, $base-font-size)}rem solid getThemifyVariable('hint-arrow-focus-outline-color');
+ @include themify() {
+ outline: #{math.div(3, $base-font-size)}rem solid
+ getThemifyVariable('hint-arrow-focus-outline-color');
outline-offset: #{math.div(-3, $base-font-size)}rem;
}
}
+ }
- .no-link-placeholder {
+ .cm-completionRefLink--disabled,
+ li:not(.has-doc-link) .cm-completionRefLink {
+ @include themify() {
background: getThemifyVariable('hint-no-link-background-color');
- pointer-events: none;
+ color: getThemifyVariable('hint-arrow-color');
}
-
- li.CodeMirror-hint-active:not(.unfocused) {
- background: getThemifyVariable('hint-item-active-background-color');
- outline: getThemifyVariable('hint-item-active-outline');
- outline-offset: getThemifyVariable('hint-item-active-outline-offset');
-
- // .fun-item {
- // border-bottom: #{2 / $base-font-size}rem solid getThemifyVariable('hint-fun-active-border-bottom-color');
- // }
-
- // .var-item {
- // border-bottom: #{2 / $base-font-size}rem solid getThemifyVariable('hint-var-active-border-bottom-color');
- // }
+ }
- .hint-name {
- color: getThemifyVariable('hint-item-active-text-color');
+ // label colors
+ li.hint-type-method,
+ li.hint-type-obj {
+ .cm-completionLabel,
+ .cm-completionMatchedText {
+ @include themify() {
+ color: getThemifyVariable('hint-fun-text-color');
}
+ }
+ }
- .fun-name, .obj-name {
- background-color: getThemifyVariable('hint-fun-text-color');
- }
-
- .var-name, .boolean-name {
- background-color: getThemifyVariable('hint-var-text-color');
+ li.hint-type-variable,
+ li.hint-type-constant,
+ li.hint-type-boolean {
+ .cm-completionLabel,
+ .cm-completionMatchedText {
+ @include themify() {
+ color: getThemifyVariable('hint-var-text-color');
}
+ }
+ }
- .keyword-name {
- background-color: getThemifyVariable('hint-keyword-text-color');
- }
-
- .hint-type, .plain-hint-item {
- color: getThemifyVariable('hint-item-active-type-text-color');
+ li.hint-type-keyword {
+ .cm-completionLabel,
+ .cm-completionMatchedText {
+ @include themify() {
+ color: getThemifyVariable('hint-keyword-text-color');
}
}
-
- .CodeMirror-hint:hover:not(.CodeMirror-hint-active) {
- background: getThemifyVariable('hint-item-hover-background-color');
- }
}
- .CodeMirror-hint {
- display: flex;
- align-items: center;
- justify-content: space-between;
-
- position: relative;
- margin: 0;
- padding: 0;
- height: 2rem;
- white-space: pre;
- cursor: pointer;
-
- &:has(.focused-hint-link) {
- z-index: 999;
+ // highlighted completion label colors
+ li.hint-type-method[aria-selected='true'],
+ li.hint-type-obj[aria-selected='true'] {
+ .cm-completionLabel {
+ @include themify() {
+ background: getThemifyVariable('hint-fun-text-color');
+ color: getThemifyVariable('hint-item-active-text-color');
+ border-bottom: #{math.div(1, $base-font-size)}rem solid
+ getThemifyVariable('hint-fun-active-border-bottom-color');
+ }
}
+ }
- &:only-child, &:last-child {
- border-bottom: none !important;
+ li.hint-type-variable[aria-selected='true'],
+ li.hint-type-constant[aria-selected='true'],
+ li.hint-type-boolean[aria-selected='true'] {
+ .cm-completionLabel {
+ @include themify() {
+ background: getThemifyVariable('hint-var-text-color');
+ color: getThemifyVariable('hint-item-active-text-color');
+ border-bottom: #{math.div(1, $base-font-size)}rem solid
+ getThemifyVariable('hint-var-active-border-bottom-color');
+ }
}
+ }
- p {
- display: flex;
- width: 100%;
- height: 100%;
- }
-
- .hint-name, .plain-hint-item {
- display: flex;
- align-items: center;
- padding: 0 0.5rem;
- width: min-content;
- font-size: 1.2rem;
- line-height: 100%;
- font-weight: bold;
- }
-
- .hint-type {
- margin: 0.5rem 2.4rem 0.5rem auto;
- font-size: 1rem;
- line-height: 100%;
- font-weight: normal;
+ li.hint-type-keyword[aria-selected='true'] {
+ .cm-completionLabel {
+ @include themify() {
+ background: getThemifyVariable('hint-keyword-text-color');
+ color: getThemifyVariable('hint-item-active-text-color');
+ }
}
+ }
- .hint-hidden {
- @extend %hidden-element;
- }
-
- a, .no-link-placeholder {
- // position: absolute;
- top: 0;
- right: 0;
- height: 100%;
- width: calc(2rem - #{math.div(1, $base-font-size)}rem);
- margin: 0;
- padding-top: 0.4rem;
- font-size: 1.2rem;
- line-height: 100%;
- text-align: center;
- outline: none;
- z-index: 1;
- }
-
- a:focus, a:active {
- outline: 0;
- }
+ .cm-completionWarning {
+ grid-column: 1 / 4;
+ display: inline-flex;
+ align-items: center;
+ gap: 0.35rem;
+ width: fit-content;
+ margin: 0.35rem 0.75rem 0.5rem 0.75rem;
+ padding: 0.32rem 0.55rem;
+ border: 1px solid #d9a300;
+ border-radius: 0.45rem;
+ background: #f3e7b7;
+ color: #9a6b00;
+ font-size: 0.8rem;
+ line-height: 1.1;
+ white-space: nowrap;
}
- .CodeMirror-hint.blacklisted {
- height: auto;
- min-height: 3.2rem; // enough to show the warning + content
+ .cm-completionWarningIcon {
+ flex: 0 0 auto;
+ font-size: 1rem;
}
-}
-// Inline hinter
-.CodeMirror-widget {
- line-height: inherit;
+ .cm-completionWarningText {
+ line-height: 1.1;
+ }
- @include themify() {
- .autocomplete-inline-hinter {
- // make the border left look like a cursor and animate like a cursor
- // border-left: #{1.2 / $base-font-size}rem solid getThemifyVariable(hint-inline-text-color);
- // animation: inline-hint-caret-blink 1s step-end infinite;
- pointer-events: none;
-
- .inline-hinter-suggestion {
- color: getThemifyVariable(hint-inline-text-color);
- font-style: italic;
- }
+ .cm-ghostCompletion {
+ @include themify() {
+ color: getThemifyVariable('hint-inline-text-color');
+ }
+ }
- .inline-hinter-suggestion-light {
- color: getThemifyVariable(hint-inline-text-color-light);
- font-style: italic;
- }
+ .cm-ghostCompletion.cm-ghostCompletion--light {
+ @include themify() {
+ color: getThemifyVariable('hint-inline-text-color-light');
}
}
-}
-@keyframes inline-hint-caret-blink {
- 50% { border-color: transparent; }
+ .hint-hidden {
+ @include themify() {
+ @extend %hidden-element;
+ display: none;
+ }
+ }
}
diff --git a/client/styles/components/_p5-contrast-codemirror-theme.scss b/client/styles/components/_p5-contrast-codemirror-theme.scss
deleted file mode 100644
index 93c604d0cc..0000000000
--- a/client/styles/components/_p5-contrast-codemirror-theme.scss
+++ /dev/null
@@ -1,152 +0,0 @@
-// brown: #6C4D13
-// black: #333
-// blue: #0F9DD7
-// pink: #D9328F
-// gray: #999999
-// dark blue: #318094
-// white: #fdfdfd
-
-//numbers
-//light gray: #f4f4f4
-//dark gray: #b5b5b5
-
-@use "sass:math";
-
-$p5-contrast-black: #1C1C1C;
-$p5-contrast-gray: #A0A0A0;
-$p5-contrast-white: #FDFDFD;
-$p5-contrast-darkgray: #333333;
-$p5-contrast-lightgray: #C1C1C1;
-$p5-contrast-blue: #00FFFF;
-$p5-contrast-green: #2DE9B6;
-$p5-contrast-yellow: #F5DC23;
-$p5-contrast-orange: #FFA95D;
-$p5-contrast-pink: #FFA9D9;
-
-$p5-contrast-gutter: #454545;
-$p5-contrast-number: #FDFDFD;
-$p5-contrast-selected: $middle-dark;
-$p5-contrast-activeline: #999999;
-
-.cm-s-p5-contrast {
- background-color: $p5-contrast-black;
- color: $p5-contrast-white;
-}
-
-.cm-s-p5-contrast span .cm-comment {
- color: $p5-contrast-lightgray;
-}
-
-.cm-s-p5-contrast span .cm-def {
- color: $p5-contrast-blue;
-}
-
-.cm-s-p5-contrast span .cm-string {
- color: $p5-contrast-green;
-}
-
-.cm-s-p5-contrast span .cm-string-2 {
- color: $p5-contrast-green;
-}
-
-.cm-s-p5-contrast span .cm-number {
- color: $p5-contrast-pink;
-}
-
-.cm-s-p5-contrast span .cm-keyword {
- color: $p5-contrast-yellow;
-}
-
-.cm-s-p5-contrast span .cm-variable {
- color: $p5-contrast-white;
-}
-
-.cm-s-p5-contrast span .cm-variable-2 {
- color: $p5-contrast-white;
-}
-
-.cm-s-p5-contrast span .cm-property {
- color: $p5-contrast-white;
-}
-
-.cm-s-p5-contrast span .cm-atom {
- color: $p5-contrast-pink;
-}
-
-.cm-s-p5-contrast span .cm-operator {
- color: $p5-contrast-lightgray;
-}
-
-.cm-s-p5-contrast .cm-linenumber {
- color: $p5-contrast-number;
-}
-
-.cm-s-p5-contrast {
- .CodeMirror-selected { background: $p5-contrast-selected; }
- .CodeMirror-focused .CodeMirror-selected { background: $p5-contrast-selected; }
- .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: $p5-contrast-selected; }
- .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: $p5-contrast-selected; }
-}
-
-.cm-s-p5-contrast .CodeMirror-activeline-background {
- background-color: $dark;
-}
-
-.cm-s-p5-contrast .CodeMirror-activeline-gutter {
- background-color: $dark;
- border-right: 1px solid $middle-dark;
-}
-
-.cm-s-p5-contrast .cm-error {
- color: #f00;
-}
-
-.cm-s-p5-contrast span .CodeMirror-matchingbracket {
- outline: 1px solid $p5-contrast-lightgray;
- outline-offset: 1px;
- color: $p5-contrast-white !important;
-}
-
-.cm-s-p5-contrast span .cm-qualifier {
- color: $p5-contrast-yellow;
-}
-
-.cm-s-p5-contrast span .cm-tag {
- color: $p5-contrast-orange;
-}
-
-.cm-s-p5-contrast span .cm-builtin {
- color: $p5-contrast-yellow;
-}
-
-.cm-s-p5-contrast span .cm-attribute {
- color: $p5-contrast-white;
-}
-
-.cm-s-p5-contrast .cm-p5-function {
- color: $p5-contrast-blue;
-}
-
-.cm-s-p5-contrast .cm-p5-variable {
- color: $p5-contrast-pink;
- font-weight: bold;
-}
-
-.cm-s-p5-contrast .CodeMirror-foldmarker {
- background-color: white;
- color: #333;
-}
-
-.cm-s-p5-contrast .CodeMirror-cursor {
- border-left: 1px solid $p5-contrast-white;
-}
-
-.cm-s-p5-contrast .cm-searching {
- // background-color: $p5js-pink-opacity;
- background-color: $medium-dark;
-}
-
-.cm-s-p5-contrast .cm-searching.CodeMirror-selectedtext {
- // background-color: $medium-dark;
- outline: #{math.div(1, $base-font-size)}rem solid $p5-contrast-white;
-}
diff --git a/client/styles/components/_p5-dark-codemirror-theme.scss b/client/styles/components/_p5-dark-codemirror-theme.scss
deleted file mode 100644
index 77940fa188..0000000000
--- a/client/styles/components/_p5-dark-codemirror-theme.scss
+++ /dev/null
@@ -1,151 +0,0 @@
-// brown: #6C4D13
-// black: #333
-// blue: #0F9DD7
-// pink: #D9328F
-// gray: #999999
-// dark blue: #318094
-// white: #fdfdfd
-
-//numbers
-//light gray: #f4f4f4
-//dark gray: #b5b5b5
-
-$p5-dark-lightbrown: #A67F59;
-$p5-light-green: #42F48F;
-$p5-dark-black: #1C1C1C;
-$p5-dark-pink: #DE4A9B;
-$p5-dark-gray: #9B9B9B;
-$p5-dark-lightblue: #0F9DD7;
-$p5-dark-darkblue: #318094;
-$p5-dark-white: #FDFDFD;
-$p5-dark-orange: #EE9900;
-$p5-dark-lightgray: #E0D7D1;
-$p5-dark-darkgray: #666666;
-$p5-dark-green: #58a10b;
-$p5-dark-goldbrown: #b58318;
-
-$p5-dark-gutter: #f4f4f4;
-$p5-dark-number: #b5b5b5;
-$p5-dark-selected: $medium-dark;
-$p5-dark-activeline: rgb(207, 207, 207);
-
-$p5-dark-error: #df3a3d;
-
-.cm-s-p5-dark {
- background-color: $p5-dark-black;
- color: $p5-dark-white;
-}
-
-.cm-s-p5-dark span.cm-comment {
- color: $p5-dark-gray;
-}
-
-.cm-s-p5-dark span.cm-def {
- color: $p5-dark-lightblue;
-}
-
-.cm-s-p5-dark span.cm-string {
- color: $p5-dark-green;
-}
-
-.cm-s-p5-dark span.cm-string-2 {
- color: $p5-dark-orange;
-}
-
-.cm-s-p5-dark span.cm-number {
- color: $p5-dark-white;
-}
-
-.cm-s-p5-dark span.cm-keyword {
- color: $p5-dark-goldbrown;
-}
-
-.cm-s-p5-dark span.cm-variable {
- color: $p5-dark-lightblue;
-}
-
-.cm-s-p5-dark span.cm-variable-2 {
- color: $p5-dark-white;
-}
-
-.cm-s-p5-dark span.cm-property {
- color: $p5-dark-white;
-}
-
-.cm-s-p5-dark span.cm-atom {
- color: $p5-dark-pink;
-}
-
-.cm-s-p5-dark span.cm-operator {
- color: $p5-dark-white;
-}
-
-.cm-s-p5-dark .cm-linenumber {
- color: $p5-dark-number;
-}
-
-.cm-s-p5-dark {
- .CodeMirror-selected { background: $p5-dark-selected; }
- .CodeMirror-focused .CodeMirror-selected { background: $p5-dark-selected; }
- .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: $p5-dark-selected; }
- .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: $p5-dark-selected; }
-}
-
-.cm-s-p5-dark .CodeMirror-activeline-background {
- background-color: $dark;
-}
-
-.cm-s-p5-dark .CodeMirror-activeline-gutter {
- background-color: $dark;
- border-right: 1px solid $middle-dark;
-}
-
-.cm-s-p5-dark span.CodeMirror-matchingbracket {
- outline: 1px solid $p5-dark-gray;
- outline-offset: 1px;
- color: $p5-dark-white !important;
-}
-
-.cm-s-p5-dark span.cm-qualifier {
- color: $p5-dark-lightblue;
-}
-
-.cm-s-p5-dark span.cm-tag {
- color: $p5-dark-pink;
-}
-
-.cm-s-p5-dark span.cm-error {
- color: $p5-dark-error;
-}
-
-.cm-s-p5-dark span.cm-builtin {
- color: $p5-dark-lightblue;
- font-weight: bold;
-}
-
-.cm-s-p5-dark span.cm-attribute {
- color: $p5-dark-lightblue;
-}
-
-.cm-s-p5-dark .cm-p5-function {
- color: $p5-dark-lightblue;
- font-weight: bold !important;
-}
-
-.cm-s-p5-dark .cm-p5-variable {
- color: $p5-dark-pink;
- font-weight: bold;
-}
-
-.cm-s-p5-dark .CodeMirror-foldmarker {
- background-color: white;
- color: #333;
-}
-
-.cm-s-p5-dark .CodeMirror-cursor {
- border-left: 1px solid $p5-dark-white;
-}
-
-.cm-s-p5-dark .cm-searching {
- background-color: $p5js-pink-opacity;
-}
diff --git a/client/styles/components/_p5-light-codemirror-theme.scss b/client/styles/components/_p5-light-codemirror-theme.scss
deleted file mode 100644
index c2e8062865..0000000000
--- a/client/styles/components/_p5-light-codemirror-theme.scss
+++ /dev/null
@@ -1,144 +0,0 @@
-// brown: #6C4D13
-// black: #333
-// blue: #0F9DD7
-// pink: #D9328F
-// gray: #999999
-// dark blue: #318094
-// white: #fdfdfd
-
-//numbers
-//light gray: #f4f4f4
-//dark gray: #b5b5b5
-
-$p5-light-brown: #7A5A3A;
-$p5-light-black: #333333;
-$p5-light-pink: #D52889;
-$p5-light-gray: #666;
-$p5-light-blue: #0B7CA9;
-$p5-light-white: $lighter;
-$p5-light-orange: #A06801;
-$p5-light-lightgray: $middle-gray;
-$p5-light-green: #47820A;
-
-
-$p5-light-gutter: #f4f4f4;
-$p5-light-number: #b5b5b5;
-$p5-light-selected: $medium-light;
-$p5-light-activeline: rgb(207, 207, 207);
-
-.cm-s-p5-light {
- background-color: $p5-light-white;
- color: $p5-light-black;
-}
-
-.cm-s-p5-light span .cm-comment {
- color: $p5-light-lightgray;
-}
-
-.cm-s-p5-light span .cm-def {
- color: $p5-light-blue;
-}
-
-.cm-s-p5-light span .cm-string {
- color: $p5-light-green;
-}
-
-.cm-s-p5-light span .cm-string-2 {
- color: $p5-light-orange;
-}
-
-.cm-s-p5-light span .cm-number {
- color: $p5-light-black;
-}
-
-.cm-s-p5-light .cm-keyword {
- color: $p5-light-brown;
-}
-
-.cm-s-p5-light span .cm-variable {
- color: $p5-light-blue;
-}
-
-.cm-s-p5-light span .cm-variable2 {
- color: $p5-light-black;
-}
-
-.cm-s-p5-light span .cm-property {
- color: $p5-light-black;
-}
-
-.cm-s-p5-light span .cm-atom {
- color: $p5-light-pink;
-}
-
-.cm-s-p5-light span .cm-operator {
- color: $p5-light-brown;
-}
-
-.cm-s-p5-light .cm-linenumber {
- color: $p5-light-number;
-}
-
-.cm-s-p5-light {
- .CodeMirror-selected { background: $p5-light-selected; }
- .CodeMirror-focused .CodeMirror-selected { background: $p5-light-selected; }
- .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: $p5-light-selected; }
- .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: $p5-light-selected; }
-}
-
-.cm-s-p5-light .CodeMirror-activeline-background {
- background-color: $light;
-}
-
-.cm-s-p5-light .CodeMirror-activeline-gutter {
- background-color: $light;
- border-right: 1px solid $medium-light;
-}
-
-.cm-s-p5-light .cm-error {
- color: #f00;
-}
-
-.cm-s-p5-light span .CodeMirror-matchingbracket {
- outline: 1px solid $p5-light-gray;
- outline-offset: 1px;
- color: $p5-light-black !important;
-}
-
-.cm-s-p5-light span .cm-qualifier {
- color: $p5-light-blue;
-}
-
-.cm-s-p5-light span .cm-tag {
- color: $p5-light-pink;
-}
-
-.cm-s-p5-light span .cm-builtin {
- color: $p5-light-blue;
-}
-
-.cm-s-p5-light span .cm-attribute {
- color: $p5-light-black;
-}
-
-.cm-s-p5-light .cm-p5-function {
- color: $p5-light-blue;
- font-weight: bold;
-}
-
-.cm-s-p5-light .cm-p5-variable {
- color: $p5-light-pink;
-}
-
-.cm-s-p5-light .CodeMirror-foldmarker {
- background-color: #333;
- color: white;
-}
-
-.cm-s-p5-light .CodeMirror-cursor {
- border-left: 1px solid $p5-light-black;
-}
-
-.cm-s-p5-light .cm-searching {
- background-color: $p5js-pink-opacity;
-}
diff --git a/client/styles/main.scss b/client/styles/main.scss
index 400da75bfb..3d1a0cbe07 100644
--- a/client/styles/main.scss
+++ b/client/styles/main.scss
@@ -6,15 +6,9 @@
@import 'base/reset';
@import 'base/base';
-@import '~codemirror/lib/codemirror';
-@import '~codemirror/addon/lint/lint';
-@import '~codemirror-colorpicker/addon/codemirror-colorpicker';
@import '~dropzone/dist/dropzone';
@import '~primer-tooltips/build/build';
-@import 'components/p5-light-codemirror-theme';
-@import 'components/p5-dark-codemirror-theme';
-@import 'components/p5-contrast-codemirror-theme';
@import 'components/account';
@import 'components/api-key';
@import 'components/editor';
diff --git a/client/testData/testReduxStore.ts b/client/testData/testReduxStore.ts
index 0a150ea2ef..4edbc0c6e4 100644
--- a/client/testData/testReduxStore.ts
+++ b/client/testData/testReduxStore.ts
@@ -46,7 +46,6 @@ const initialTestState: RootState = {
justOpenedProject: false,
previousPath: '/',
errorType: undefined,
- runtimeErrorWarningVisible: true,
parentId: undefined
},
files: initialFilesState(),
@@ -92,8 +91,14 @@ const initialTestState: RootState = {
list: [],
totalSize: 0
},
- loading: false,
- collections: [],
+// <<<<<<< HEAD
+ loading: false,
+ collections: [],
+ collectionsListCollections: {
+ collections: [],
+// =======
+},
+ // loading: false,
collectionsListProjects: {
projects: [],
metadata: {
@@ -104,6 +109,5 @@ const initialTestState: RootState = {
hasPagination: true
}
}
-};
-
+}
export { mockProjects, initialTestState };
diff --git a/client/utils/codemirror-search.js b/client/utils/codemirror-search.js
deleted file mode 100644
index 3ed3116a63..0000000000
--- a/client/utils/codemirror-search.js
+++ /dev/null
@@ -1,830 +0,0 @@
-/* eslint-disable */
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
-
-// Define search commands. Depends on dialog.js or another
-// implementation of the openDialog method.
-
-// Replace works a little oddly -- it will do the replace on the next
-// Ctrl-G (or whatever is bound to findNext) press. You prevent a
-// replace by making sure the match is no longer selected when hitting
-// Ctrl-G.
-import i18n from '../i18n';
-import CodeMirror from 'codemirror';
-import triangleArrowRight from '../images/triangle-arrow-right.svg?byContent';
-import triangleArrowDown from '../images/triangle-arrow-down.svg?byContent';
-import downArrow from '../images/down-arrow.svg?byContent';
-import upArrow from '../images/up-arrow.svg?byContent';
-import exitIcon from '../images/exit.svg?byContent';
-
-function searchOverlay(query, caseInsensitive) {
- if (typeof query == 'string') {
- query = new RegExp(
- query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'),
- caseInsensitive ? 'gi' : 'g'
- );
- } else if (!query.global) {
- query = new RegExp(query.source, query.ignoreCase ? 'gi' : 'g');
- }
-
- return {
- token: function (stream) {
- query.lastIndex = stream.pos;
- var match = query.exec(stream.string);
- if (match && match.index == stream.pos) {
- stream.pos += match[0].length || 1;
- return 'searching';
- } else if (match) {
- stream.pos = match.index;
- } else {
- stream.skipToEnd();
- }
- }
- };
-}
-
-function SearchState() {
- this.posFrom = this.posTo = this.lastQuery = this.query = null;
- this.overlay = null;
- this.regexp = false;
- this.caseInsensitive = true;
- this.wholeWord = false;
- this.replaceStarted = false;
- this.lastFileName =
- document.querySelector('.editor__file-name span')?.innerText || null;
-}
-
-function getSearchState(cm) {
- return cm.state.search || (cm.state.search = new SearchState());
-}
-
-function getSearchCursor(cm, query, pos) {
- // Heuristic: if the query string is all lowercase, do a case insensitive search.
- return cm.getSearchCursor(query, pos, getSearchState(cm).caseInsensitive);
-}
-
-function watchFileChanges(cm, searchState, searchField) {
- let observer = null;
-
- function setupObserver() {
- var fileNameElement = document.querySelector('.editor__file-name span');
-
- if (!fileNameElement) {
- setTimeout(setupObserver, 500);
- return;
- }
-
- if (observer) {
- return;
- }
-
- observer = new MutationObserver(() => {
- if (searchField.value.length > 1) {
- startSearch(cm, searchState, searchField.value);
- }
- });
-
- observer.observe(fileNameElement, { characterData: true, subtree: true });
- }
-
- function disconnectObserver() {
- if (observer) {
- observer.disconnect();
- observer = null;
- }
- }
-
- setupObserver();
-
- setInterval(() => {
- var searchDialog = document.querySelector('.CodeMirror-dialog');
- if (!searchDialog && observer) {
- disconnectObserver();
- return;
- } else if (searchDialog && !observer) {
- setupObserver();
- }
- }, 500);
-}
-
-function isMouseClick(event) {
- if (event.detail > 0) return true;
- else return false;
-}
-
-function persistentDialog(cm, text, deflt, onEnter, replaceOpened, onKeyDown) {
- var searchField = document.getElementsByClassName(
- 'CodeMirror-search-field'
- )[0];
- if (!searchField) {
- cm.openDialog(text, onEnter, {
- value: deflt,
- selectValueOnOpen: true,
- closeOnEnter: false,
- onClose: function () {
- clearSearch(cm);
- },
- onKeyDown: onKeyDown,
- closeOnBlur: false
- });
-
- searchField = document.getElementById('Find-input-field');
-
- var dialog = document.getElementsByClassName('CodeMirror-dialog')[0];
- var closeButton = dialog.getElementsByClassName('close')[0];
-
- var state = getSearchState(cm);
-
- watchFileChanges(cm, getSearchState(cm), searchField);
-
- CodeMirror.on(searchField, 'keyup', function (e) {
- state.replaceStarted = false;
- if (e.keyCode !== 13 && searchField.value.length > 1) {
- startSearch(cm, getSearchState(cm), searchField.value);
- } else if (searchField.value.length < 1) {
- cm.display.wrapper.querySelector(
- '.CodeMirror-search-results'
- ).innerText = i18n.t('CodemirrorFindAndReplace.NoResults');
- }
- });
-
- CodeMirror.on(closeButton, 'click', function () {
- dialog.parentNode.removeChild(dialog);
- clearSearch(cm);
- cm.focus();
- });
-
- var upArrow = dialog.getElementsByClassName('up-arrow')[0];
- CodeMirror.on(upArrow, 'click', function () {
- if (searchField.value.trim() === '') {
- searchField.focus();
- } else {
- cm.focus();
- CodeMirror.commands.findPrev(cm);
- searchField.blur();
- }
- });
-
- var downArrow = dialog.getElementsByClassName('down-arrow')[0];
- CodeMirror.on(downArrow, 'click', function () {
- if (searchField.value.trim() === '') {
- searchField.focus();
- } else {
- cm.focus();
- CodeMirror.commands.findNext(cm);
- searchField.blur();
- }
- });
-
- var regexpButton = dialog.getElementsByClassName(
- 'CodeMirror-regexp-button'
- )[0];
- CodeMirror.on(regexpButton, 'click', function (event) {
- var state = getSearchState(cm);
- state.regexp = toggle(regexpButton);
- startSearch(cm, getSearchState(cm), searchField.value);
- if (isMouseClick(event)) searchField.focus();
- });
-
- toggle(regexpButton, state.regexp);
-
- var caseSensitiveButton = dialog.getElementsByClassName(
- 'CodeMirror-case-button'
- )[0];
- CodeMirror.on(caseSensitiveButton, 'click', function (event) {
- var state = getSearchState(cm);
- state.caseInsensitive = !toggle(caseSensitiveButton);
- startSearch(cm, getSearchState(cm), searchField.value);
- if (isMouseClick(event)) searchField.focus();
- });
-
- toggle(caseSensitiveButton, !state.caseInsensitive);
-
- var wholeWordButton = dialog.getElementsByClassName(
- 'CodeMirror-word-button'
- )[0];
- CodeMirror.on(wholeWordButton, 'click', function (event) {
- var state = getSearchState(cm);
- state.wholeWord = toggle(wholeWordButton);
- startSearch(cm, getSearchState(cm), searchField.value);
- if (isMouseClick(event)) searchField.focus();
- });
-
- toggle(wholeWordButton, state.wholeWord);
-
- function toggle(el, initialState) {
- var currentState, nextState;
-
- if (initialState == null) {
- currentState = el.getAttribute('aria-checked') === 'true';
- nextState = !currentState;
- } else {
- nextState = initialState;
- }
-
- el.setAttribute('aria-checked', nextState);
- return nextState;
- }
-
- function toggleReplace(open) {
- var toggleButtonHeightOpened = '80px',
- toggleButtonHeightClosed = '40px';
-
- if (open) {
- replaceFieldDiv.style.display = replaceControlsDiv.style.display = '';
- toggleReplaceBtnDiv.style.height = toggleButtonHeightOpened;
- toggleReplaceBtn.style.height = toggleButtonHeightOpened;
- toggleReplaceBtn.innerHTML = triangleArrowDown;
- } else {
- replaceFieldDiv.style.display = replaceControlsDiv.style.display =
- 'none';
- toggleReplaceBtnDiv.style.height = toggleButtonHeightClosed;
- toggleReplaceBtn.style.height = toggleButtonHeightClosed;
- toggleReplaceBtn.innerHTML = triangleArrowRight;
- }
- }
-
- var toggleReplaceBtnDiv = document.getElementById('Btn-Toggle-replace-div');
- var toggleReplaceBtn = document.getElementById('Btn-Toggle-replace');
- var replaceFieldDiv = document.getElementById('Replace-input-div');
- var replaceControlsDiv = document.getElementById('Replace-controls-div');
- if (replaceOpened) {
- toggleReplace(true);
- }
- CodeMirror.on(toggleReplaceBtn, 'click', function () {
- if (replaceFieldDiv.style.display === 'none') {
- toggleReplace(true);
- } else {
- toggleReplace(false);
- }
- });
-
- var replaceField = document.getElementById('Replace-input-field');
- CodeMirror.on(replaceField, 'keyup', function (e) {
- var state = getSearchState(cm);
- var query = parseQuery(searchField.value, state);
- var withText = parseString(replaceField.value);
- if (e.keyCode === 13) {
- // if enter
- var cursor = getSearchCursor(cm, query, cm.getCursor('from'));
- var start = cursor.from();
- var match = cursor.findNext();
- if (!match) {
- cursor = getSearchCursor(cm, query);
- if (
- !(match = cursor.findNext()) ||
- (start &&
- cursor.from().line == start.line &&
- cursor.from().ch == start.ch)
- )
- return;
- }
- cm.setSelection(cursor.from(), cursor.to());
- state.replaceStarted = true;
- doReplace(match, cursor, query, withText);
- }
- });
-
- function doReplace(match, cursor, query, withText) {
- cursor.replace(
- typeof query == 'string'
- ? withText
- : withText.replace(/\$(\d)/g, function (_, i) {
- return match[i];
- })
- );
- cursor.findNext();
- // cm.focus();
- CodeMirror.commands.findNext(cm);
- // searchField.blur();
- }
-
- var doReplaceButton = document.getElementById('Btn-replace');
- CodeMirror.on(doReplaceButton, 'click', function (e) {
- if (!searchField.value) {
- searchField.focus();
- return;
- }
- var state = getSearchState(cm);
- var query = parseQuery(searchField.value, state);
- var withText = parseString(replaceField.value);
- if (state.replaceStarted) {
- var cursor = getSearchCursor(cm, query, cm.getCursor('from'));
- var match = cursor.findNext();
- if (match) {
- cm.setSelection(cursor.from(), cursor.to());
- doReplace(match, cursor, query, withText);
- doReplaceButton.focus();
- }
- } else {
- startSearch(cm, state, searchField.value);
- state.replaceStarted = true;
- cm.focus();
- CodeMirror.commands.findNext(cm);
- searchField.blur();
- doReplaceButton.focus();
- }
- });
-
- var doReplaceAllButton = document.getElementById('Btn-replace-all');
- CodeMirror.on(doReplaceAllButton, 'click', function (e) {
- if (!searchField.value) {
- searchField.focus();
- return;
- }
- var state = getSearchState(cm);
- var query = parseQuery(searchField.value, state);
- var withText = parseString(replaceField.value);
- if (searchField.value.length > 1) {
- state.replaceStarted = true;
- }
- if (state.replaceStarted) {
- replaceAll(cm, query, withText);
- state.replaceStarted = false;
- } else {
- startSearch(cm, state, searchField.value);
- state.replaceStarted = true;
- }
- });
- } else {
- searchField.value = deflt;
-
- searchField.focus();
- searchField.select();
- }
-}
-
-function dialog(cm, text, shortText, deflt, f) {
- if (cm.openDialog)
- cm.openDialog(text, f, { value: deflt, selectValueOnOpen: true });
- else f(prompt(shortText, deflt));
-}
-
-function parseString(string) {
- return string.replace(/\\(.)/g, function (_, ch) {
- if (ch == 'n') return '\n';
- if (ch == 'r') return '\r';
- return ch;
- });
-}
-
-function parseQuery(query, state) {
- var emptyQuery = 'x^'; // matches nothing
- if (query === '') {
- query = emptyQuery;
- } else {
- if (state.regexp === false) {
- query = parseString(query);
- query = query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
- }
- if (state.wholeWord) {
- query = '\\b' + query + '\\b';
- }
- }
-
- var regexp;
- try {
- regexp = new RegExp(query, state.caseInsensitive ? 'gi' : 'g');
- } catch (e) {
- regexp = new RegExp(emptyQuery, 'g');
- }
- // If the resulting regexp will match everything, do not use it
- if (regexp.test('')) {
- return new RegExp(emptyQuery, 'g');
- }
- return regexp;
-}
-
-function startSearch(cm, state, query) {
- var searchDialog = document.querySelector('.CodeMirror-dialog');
- if (searchDialog) {
- // check if the file has changed
- let currentFileName = document.querySelector('.editor__file-name span')
- ?.innerText;
-
- if (state.lastFileName !== currentFileName) {
- state.lastFileName = currentFileName;
- state.queryText = null;
- state.lastQuery = null;
- state.query = null;
- cm.removeOverlay(state.overlay);
- state.overlay = null;
-
- if (searchDialog) {
- cm.display.wrapper.querySelector(
- '.CodeMirror-search-results'
- ).innerText = '0/0';
- }
- }
-
- state.queryText = query;
- state.lastQuery = query;
- state.query = parseQuery(query, state);
- cm.removeOverlay(state.overlay, state.caseInsensitive);
- state.overlay = searchOverlay(state.query, state.caseInsensitive);
- cm.addOverlay(state.overlay);
- if (cm.showMatchesOnScrollbar) {
- if (state.annotate) {
- state.annotate.clear();
- state.annotate = null;
- }
- state.annotate = cm.showMatchesOnScrollbar(
- state.query,
- state.caseInsensitive
- );
- }
-
- var cursor = getSearchCursor(cm, state.query);
- cursor.findNext();
- var num_match = cm.state.search.annotate.matches.length;
- if (num_match == 0) {
- cm.display.wrapper.querySelector(
- '.CodeMirror-search-results'
- ).innerText = i18n.t('CodemirrorFindAndReplace.NoResults');
- cm.removeOverlay(state.overlay, state.caseInsensitive);
- } else {
- var next =
- cm.state.search.annotate.matches.findIndex((s) => {
- return (
- s.from.ch === cursor.from().ch && s.from.line === cursor.from().line
- );
- }) + 1;
- var text_match = next + '/' + num_match;
- cm.display.wrapper.querySelector(
- '.CodeMirror-search-results'
- ).innerText = text_match;
- }
- }
-}
-
-function doSearch(cm, rev, persistent, immediate, ignoreQuery) {
- var state = getSearchState(cm);
- if (!ignoreQuery && state.query) {
- return findNext(cm, rev);
- }
- var q = cm.getSelection() || state.lastQuery;
- var queryDialog = getQueryDialog();
- if (persistent && cm.openDialog) {
- var hiding = null;
- var searchNext = function (query, event) {
- CodeMirror.e_stop(event);
- if (!query) return;
- if (query != state.queryText) {
- startSearch(cm, state, query);
- state.posFrom = state.posTo = cm.getCursor();
- }
- if (hiding) hiding.style.opacity = 1;
- findNext(cm, event.shiftKey, function (_, to) {
- var dialog;
- if (
- to.line < 3 &&
- document.querySelector &&
- (dialog = cm.display.wrapper.querySelector('.CodeMirror-dialog')) &&
- dialog.getBoundingClientRect().bottom - 4 >
- cm.cursorCoords(to, 'window').top
- )
- (hiding = dialog).style.opacity = 0.4;
- });
- };
- persistentDialog(
- cm,
- queryDialog,
- q,
- searchNext,
- false,
- function (event, query) {
- var keyName = CodeMirror.keyName(event);
- var cmd = CodeMirror.keyMap[cm.getOption('keyMap')][keyName];
- if (!cmd) cmd = cm.getOption('extraKeys')[keyName];
- if (
- cmd == 'findNext' ||
- cmd == 'findPrev' ||
- cmd == 'findPersistentNext' ||
- cmd == 'findPersistentPrev'
- ) {
- CodeMirror.e_stop(event);
- startSearch(cm, getSearchState(cm), query);
- cm.execCommand(cmd);
- } else if (cmd == 'find' || cmd == 'findPersistent') {
- CodeMirror.e_stop(event);
- searchNext(query, event);
- }
- }
- );
- if (immediate && q) {
- startSearch(cm, state, q);
- findNext(cm, rev);
- }
- cm.on('change', function () {
- var state = getSearchState(cm);
- if (state.query) {
- startSearch(cm, state, state.queryText);
- }
- });
- } else {
- dialog(cm, queryDialog, 'Search for:', q, function (query) {
- if (query && !state.query)
- cm.operation(function () {
- startSearch(cm, state, query);
- state.posFrom = state.posTo = cm.getCursor();
- findNext(cm, rev);
- });
- });
- }
-}
-
-function doFindAndReplace(
- cm,
- rev,
- persistent,
- immediate,
- ignoreQuery,
- replaceOpened
-) {
- var state = getSearchState(cm);
- if (!ignoreQuery && state.query) {
- return findNext(cm, rev);
- }
- var q = cm.getSelection() || state.lastQuery;
- var queryDialog = getQueryDialog();
- if (persistent && cm.openDialog) {
- var hiding = null;
- var searchNext = function (query, event) {
- CodeMirror.e_stop(event);
- if (!query) return;
- if (query != state.queryText) {
- startSearch(cm, state, query);
- state.posFrom = state.posTo = cm.getCursor();
- }
- if (hiding) hiding.style.opacity = 1;
- findNext(cm, event.shiftKey, function (_, to) {
- var dialog;
- if (
- to.line < 3 &&
- document.querySelector &&
- (dialog = cm.display.wrapper.querySelector('.CodeMirror-dialog')) &&
- dialog.getBoundingClientRect().bottom - 4 >
- cm.cursorCoords(to, 'window').top
- )
- (hiding = dialog).style.opacity = 1;
- });
- };
- persistentDialog(
- cm,
- queryDialog,
- q,
- searchNext,
- replaceOpened,
- function (event, query) {
- var keyName = CodeMirror.keyName(event);
- var cmd = CodeMirror.keyMap[cm.getOption('keyMap')][keyName];
- if (!cmd) cmd = cm.getOption('extraKeys')[keyName];
- if (
- cmd == 'findNext' ||
- cmd == 'findPrev' ||
- cmd == 'findPersistentNext' ||
- cmd == 'findPersistentPrev'
- ) {
- CodeMirror.e_stop(event);
- startSearch(cm, getSearchState(cm), query);
- cm.execCommand(cmd);
- } else if (cmd == 'find' || cmd == 'findPersistent') {
- CodeMirror.e_stop(event);
- searchNext(query, event);
- }
- }
- );
- if (immediate && q) {
- startSearch(cm, state, q);
- findNext(cm, rev);
- }
- } else {
- dialog(cm, queryDialog, 'Search for:', q, function (query) {
- if (query && !state.query)
- cm.operation(function () {
- startSearch(cm, state, query);
- state.posFrom = state.posTo = cm.getCursor();
- findNext(cm, rev);
- });
- });
- }
-}
-
-function findNext(cm, rev, callback) {
- cm.operation(function () {
- var state = getSearchState(cm);
- var cursor = getSearchCursor(
- cm,
- state.query,
- rev ? state.posFrom : state.posTo
- );
- if (!cursor.find(rev)) {
- cursor = getSearchCursor(
- cm,
- state.query,
- rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0)
- );
- if (!cursor.find(rev)) {
- cm.display.wrapper.querySelector(
- '.CodeMirror-search-results'
- ).innerText = i18n.t('CodemirrorFindAndReplace.NoResults');
- return;
- }
- }
- cm.setSelection(cursor.from(), cursor.to());
- cm.scrollIntoView(
- { from: cursor.from(), to: cursor.to() },
- cm.getScrollInfo().clientHeight / 2
- );
- state.posFrom = cursor.from();
- state.posTo = cursor.to();
- var num_match = cm.state.search.annotate.matches.length;
- var next =
- cm.state.search.annotate.matches.findIndex(
- (s) =>
- s.from.ch === cursor.from().ch && s.from.line === cursor.from().line
- ) + 1;
- var text_match = next + '/' + num_match;
- cm.display.wrapper.querySelector(
- '.CodeMirror-search-results'
- ).innerText = text_match;
- if (callback) callback(cursor.from(), cursor.to());
- });
-}
-
-function clearSearch(cm) {
- cm.operation(function () {
- var state = getSearchState(cm);
- state.replaceStarted = false;
- if (!state.query) return;
- state.query = state.queryText = null;
- cm.removeOverlay(state.overlay);
- if (state.annotate) {
- state.annotate.clear();
- state.annotate = null;
- }
- });
-}
-
-function replaceAll(cm, query, text) {
- cm.operation(function () {
- for (var cursor = getSearchCursor(cm, query); cursor.findNext(); ) {
- if (typeof query != 'string') {
- var match = cm.getRange(cursor.from(), cursor.to()).match(query);
- cursor.replace(
- text.replace(/\$(\d)/g, function (_, i) {
- return match[i];
- })
- );
- } else cursor.replace(text);
- }
- });
-}
-
-var getQueryDialog = function () {
- return `
-
- `;
-};
-
-// CodeMirror.commands.findPersistent = function(cm) {doFindAndReplace(cm, false, true, false, true, false);};
-// CodeMirror.commands.findPersistentNext = function(cm) {doFindAndReplace(cm, false, true, false, true, false);};
-// CodeMirror.commands.findPersistentPrev = function(cm) {doFindAndReplace(cm, false, true, false, true, false);};
-// CodeMirror.commands.findNext = doFindAndReplace;
-// CodeMirror.commands.findPrev = function(cm) {doFindAndReplace(cm, true);};
-// CodeMirror.commands.clearSearch = clearSearch;
-// CodeMirror.commands.replace = function(cm) { doFindAndReplace(cm, false, true, false, true, true); };
-
-CodeMirror.commands.find = function (cm) {
- doSearch(cm);
-};
-CodeMirror.commands.findPersistent = function (cm) {
- doSearch(cm, false, true, false, true);
-};
-CodeMirror.commands.findPersistentNext = function (cm) {
- doSearch(cm, false, true, true);
-};
-CodeMirror.commands.findPersistentPrev = function (cm) {
- doSearch(cm, true, true, true);
-};
-CodeMirror.commands.findNext = doSearch;
-CodeMirror.commands.findPrev = function (cm) {
- doSearch(cm, true);
-};
-CodeMirror.commands.clearSearch = clearSearch;
-CodeMirror.commands.replace = function (cm) {
- doFindAndReplace(cm, false, true, false, true, true);
-};
-CodeMirror.commands.replaceAll = function (cm) {
- doFindAndReplace(cm, true);
-};
diff --git a/client/utils/contextAwareHinter.js b/client/utils/contextAwareHinter.js
index a90b88cd0e..f9fc11be39 100644
--- a/client/utils/contextAwareHinter.js
+++ b/client/utils/contextAwareHinter.js
@@ -4,17 +4,145 @@ import classMap from './p5-instance-methods-and-creators.json';
const scopeMap = require('./p5-scope-function-access-map.json');
-function getExpressionBeforeCursor(cm) {
- const cursor = cm.getCursor();
- const line = cm.getLine(cursor.line);
- const uptoCursor = line.slice(0, cursor.ch);
+function getCurrentWordInfo(context) {
+ const word = context.matchBefore(/\w*/);
+
+ if (!word) {
+ return {
+ text: '',
+ from: context.pos,
+ to: context.pos
+ };
+ }
+
+ return {
+ text: word.text || '',
+ from: word.from,
+ to: context.pos
+ };
+}
+
+function getExpressionBeforeCursor(state, pos) {
+ const line = state.doc.lineAt(pos);
+ const uptoCursor = line.text.slice(0, pos - line.from);
const match = uptoCursor.match(
/([a-zA-Z_$][\w$]*(?:\.[a-zA-Z_$][\w$]*)*)\.(?:[a-zA-Z_$][\w$]*)?$/
);
return match ? match[1] : null;
}
-export default function contextAwareHinter(cm, options = {}) {
+function getTypedMemberInfo(state, pos) {
+ const line = state.doc.lineAt(pos);
+ const uptoCursor = line.text.slice(0, pos - line.from);
+ const dotMatch = uptoCursor.match(/\.([a-zA-Z_$][\w$]*)?$/);
+
+ if (!dotMatch) {
+ return {
+ typed: '',
+ from: pos,
+ to: pos
+ };
+ }
+
+ const typed = dotMatch[1] || '';
+ const methodStart = pos - dotMatch[0].length + 1;
+
+ return {
+ typed,
+ from: methodStart,
+ to: pos
+ };
+}
+
+function formatPreview(label, params = []) {
+ if (!params.length) return `${label}()`;
+
+ return `${label}(${params
+ .map((param) => (param.o ? `[${param.p}]` : param.p))
+ .join(', ')})`;
+}
+
+function makeBaseHintLookup(hints) {
+ const byLabel = new Map();
+
+ hints.forEach((hint) => {
+ if (hint?.label) {
+ byLabel.set(hint.label, hint);
+ }
+ });
+
+ return byLabel;
+}
+
+function buildMethodOption(methodName, baseHint, range) {
+ const params = baseHint?.params || [];
+
+ return {
+ label: methodName,
+ type: 'method',
+ kindLabel: baseHint?.kindLabel || 'fun',
+ params,
+ p5DocPath: baseHint?.p5DocPath,
+ preview: baseHint?.preview || formatPreview(methodName, params),
+ from: range.from,
+ to: range.to
+ };
+}
+
+function buildVarOrFunctionOption({
+ name,
+ isFunc,
+ userDefinedFunctionMetadata,
+ blacklist,
+ range
+}) {
+ const fnMeta = userDefinedFunctionMetadata[name];
+ const params = fnMeta?.params || [];
+
+ let preview;
+
+ if (isFunc) {
+ if (fnMeta?.text) {
+ preview = formatPreview(fnMeta.text, params);
+ } else {
+ preview = formatPreview(name, params);
+ }
+ } else {
+ preview = undefined;
+ }
+
+ const isBlacklisted = blacklist.includes(name);
+
+ return {
+ label: fnMeta?.text || name,
+ type: isFunc ? 'method' : 'variable',
+ kindLabel: isFunc ? 'fun' : 'var',
+ params,
+ p5DocPath: undefined,
+ preview,
+ blacklisted: isBlacklisted,
+ warning: isBlacklisted ? '⚠️ use with caution in this context' : null,
+ from: range.from,
+ to: range.to
+ };
+}
+
+function buildGlobalHintOption(hint, blacklist, range) {
+ return {
+ ...hint,
+ blacklisted: blacklist.includes(hint.label),
+ warning: blacklist.includes(hint.label)
+ ? '⚠️ use with caution in this context'
+ : null,
+ from: range.from,
+ to: range.to
+ };
+}
+
+export default function contextAwareHinter(context, { hints = [] } = {}) {
+ const { state, pos } = context;
+ const cm = state.doc.toString();
+
const {
variableToP5ClassMap = {},
scopeToDeclaredVarsMap = {},
@@ -22,12 +150,9 @@ export default function contextAwareHinter(cm, options = {}) {
userDefinedClassMetadata = {}
} = p5CodeAstAnalyzer(cm) || {};
- const { hinter } = options;
- if (!hinter || typeof hinter.search !== 'function') {
- return [];
- }
+ const baseHintLookup = makeBaseHintLookup(hints);
- const baseExpression = getExpressionBeforeCursor(cm);
+ const baseExpression = getExpressionBeforeCursor(state, pos);
if (baseExpression) {
const className = variableToP5ClassMap[baseExpression];
@@ -38,61 +163,36 @@ export default function contextAwareHinter(cm, options = {}) {
let methods = [];
if (userClassEntry?.methods) {
- const { methods: userMethods } = userClassEntry;
- methods = userMethods;
+ methods = userClassEntry.methods;
} else if (className && classMap[className]?.methods) {
- const { methods: classMethods } = classMap[className];
- methods = classMethods;
+ methods = classMap[className].methods;
} else {
return [];
}
- const cursor = cm.getCursor();
- const lineText = cm.getLine(cursor.line);
- const dotMatch = lineText
- .slice(0, cursor.ch)
- .match(/\.([a-zA-Z_$][\w$]*)?$/);
-
- let from = cursor;
- if (dotMatch) {
- const fullMatch = dotMatch[0];
- const methodStart = cursor.ch - fullMatch.length + 1;
- from = { line: cursor.line, ch: methodStart };
- } else {
- from = cursor;
- }
-
- const to = { line: cursor.line, ch: cursor.ch };
- const typed = dotMatch?.[1]?.toLowerCase() || '';
-
- const methodHints = methods
- .filter((method) => method.toLowerCase().startsWith(typed))
- .map((method) => ({
- item: {
- text: method,
- type: 'fun',
- isMethod: true
- },
- displayText: method,
- from,
- to
- }));
-
- return methodHints;
+ const memberInfo = getTypedMemberInfo(state, pos);
+ const typedLower = memberInfo.typed.toLowerCase();
+
+ const options = methods
+ .filter((method) => method.toLowerCase().startsWith(typedLower))
+ .map((method) =>
+ buildMethodOption(method, baseHintLookup.get(method), memberInfo)
+ );
+
+ return {
+ from: memberInfo.from,
+ to: memberInfo.to,
+ options,
+ filter: false
+ };
}
- const { line, ch } = cm.getCursor();
- const { string } = cm.getTokenAt({ line, ch });
- const currentWord = string.trim();
-
- const currentContext = getContext(cm);
- const allHints = hinter.search(currentWord);
+ const wordInfo = getCurrentWordInfo(context);
+ const lowerCurrentWord = wordInfo.text.toLowerCase();
- // const whitelist = scopeMap[currentContext]?.whitelist || [];
+ const currentContext = getContext(cm, pos);
const blacklist = scopeMap[currentContext]?.blacklist || [];
- const lowerCurrentWord = currentWord.toLowerCase();
-
function isInScope(varName) {
return Object.entries(scopeToDeclaredVarsMap).some(
([scope, vars]) =>
@@ -103,13 +203,13 @@ export default function contextAwareHinter(cm, options = {}) {
const allVarNames = Array.from(
new Set(
Object.values(scopeToDeclaredVarsMap)
- .map((s) => Object.keys(s))
+ .map((scopeVars) => Object.keys(scopeVars))
.flat()
.filter((name) => typeof name === 'string')
)
);
- const varHints = allVarNames
+ const localOptions = allVarNames
.filter(
(varName) =>
varName.toLowerCase().startsWith(lowerCurrentWord) && isInScope(varName)
@@ -120,70 +220,58 @@ export default function contextAwareHinter(cm, options = {}) {
(!scopeToDeclaredVarsMap[currentContext]?.[varName] &&
scopeToDeclaredVarsMap.global?.[varName] === 'fun');
- const baseItem = isFunc
- ? { ...userDefinedFunctionMetadata[varName] }
- : {
- text: varName,
- type: 'var',
- params: [],
- p5: false
- };
-
- return {
- item: baseItem,
- isBlacklisted: blacklist.includes(varName)
- };
+ return buildVarOrFunctionOption({
+ name: varName,
+ isFunc,
+ userDefinedFunctionMetadata,
+ blacklist,
+ range: wordInfo
+ });
});
- const filteredHints = allHints
+ const globalOptions = hints
.filter(
- (h) =>
- h &&
- h.item &&
- typeof h.item.text === 'string' &&
- h.item.text.toLowerCase().startsWith(lowerCurrentWord)
+ (hint) =>
+ hint &&
+ typeof hint.label === 'string' &&
+ hint.label.toLowerCase().startsWith(lowerCurrentWord)
)
- .map((hint) => {
- const name = hint.item?.text || '';
- const isBlacklisted = blacklist.includes(name);
-
- return {
- ...hint,
- isBlacklisted
- };
- });
+ .map((hint) => buildGlobalHintOption(hint, blacklist, wordInfo));
- const combinedHints = [...varHints, ...filteredHints];
+ const combinedOptions = [...localOptions, ...globalOptions];
const typePriority = {
- fun: 0,
- var: 1,
+ method: 0,
+ variable: 1,
keyword: 2,
- other: 3
+ constant: 3,
+ boolean: 4,
+ obj: 5,
+ other: 6
};
- const sorted = combinedHints.sort((a, b) => {
- const nameA = a.item?.text || '';
- const nameB = b.item?.text || '';
- const typeA = a.item?.type || 'other';
- const typeB = b.item?.type || 'other';
-
- const isBlacklistedA = a.isBlacklisted ? 1 : 0;
- const isBlacklistedB = b.isBlacklisted ? 1 : 0;
-
- const typeScoreA = typePriority[typeA] ?? typePriority.other;
- const typeScoreB = typePriority[typeB] ?? typePriority.other;
+ combinedOptions.sort((a, b) => {
+ const isBlacklistedA = a.blacklisted ? 1 : 0;
+ const isBlacklistedB = b.blacklisted ? 1 : 0;
if (isBlacklistedA !== isBlacklistedB) {
return isBlacklistedA - isBlacklistedB;
}
- if (typeScoreA !== typeScoreB) {
- return typeScoreA - typeScoreB;
+ const typeA = typePriority[a.type] ?? typePriority.other;
+ const typeB = typePriority[b.type] ?? typePriority.other;
+
+ if (typeA !== typeB) {
+ return typeA - typeB;
}
- return nameA.localeCompare(nameB);
+ return a.label.localeCompare(b.label);
});
- return sorted;
+ return {
+ from: wordInfo.from,
+ to: wordInfo.to,
+ options: combinedOptions,
+ filter: false
+ };
}
diff --git a/client/utils/getContext.js b/client/utils/getContext.js
index beddef71f7..2e9a11ebdc 100644
--- a/client/utils/getContext.js
+++ b/client/utils/getContext.js
@@ -1,11 +1,7 @@
const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
-export default function getContext(_cm) {
- const code = _cm.getValue();
- const cursor = _cm.getCursor();
- const offset = _cm.indexFromPos(cursor);
-
+export default function getContext(code, pos) {
let ast;
try {
ast = parser.parse(code, {
@@ -21,7 +17,7 @@ export default function getContext(_cm) {
traverse(ast, {
Function(path) {
const { node } = path;
- if (offset >= node.start && offset <= node.end) {
+ if (pos >= node.start && pos <= node.end) {
if (node.id && node.id.name) {
context = node.id.name;
} else {
diff --git a/client/utils/htmlmixed.js b/client/utils/htmlmixed.js
deleted file mode 100644
index 23a0eacf05..0000000000
--- a/client/utils/htmlmixed.js
+++ /dev/null
@@ -1,153 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
-/* eslint-disable */
-
-(function(mod) {
- if (typeof exports == "object" && typeof module == "object") // CommonJS
- mod(require("codemirror"), require("codemirror/mode/xml/xml"), require("./p5-javascript"), require("codemirror/mode/css/css"));
- else if (typeof define == "function" && define.amd) // AMD
- define(["codemirror", "codemirror/mode/xml/xml", "./p5-javascript", "codemirror/mode/css/css"], mod);
- else // Plain browser env
- mod(CodeMirror);
-})(function(CodeMirror) {
- "use strict";
-
- var defaultTags = {
- script: [
- ["lang", /(javascript|babel)/i, "javascript"],
- ["type", /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^$/i, "javascript"],
- ["type", /./, "text/plain"],
- [null, null, "javascript"]
- ],
- style: [
- ["lang", /^css$/i, "css"],
- ["type", /^(text\/)?(x-)?(stylesheet|css)$/i, "css"],
- ["type", /./, "text/plain"],
- [null, null, "css"]
- ]
- };
-
- function maybeBackup(stream, pat, style) {
- var cur = stream.current(), close = cur.search(pat);
- if (close > -1) {
- stream.backUp(cur.length - close);
- } else if (cur.match(/<\/?$/)) {
- stream.backUp(cur.length);
- if (!stream.match(pat, false)) stream.match(cur);
- }
- return style;
- }
-
- var attrRegexpCache = {};
- function getAttrRegexp(attr) {
- var regexp = attrRegexpCache[attr];
- if (regexp) return regexp;
- return attrRegexpCache[attr] = new RegExp("\\s+" + attr + "\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*");
- }
-
- function getAttrValue(text, attr) {
- var match = text.match(getAttrRegexp(attr))
- return match ? /^\s*(.*?)\s*$/.exec(match[2])[1] : ""
- }
-
- function getTagRegexp(tagName, anchored) {
- return new RegExp((anchored ? "^" : "") + "<\/\s*" + tagName + "\s*>", "i");
- }
-
- function addTags(from, to) {
- for (var tag in from) {
- var dest = to[tag] || (to[tag] = []);
- var source = from[tag];
- for (var i = source.length - 1; i >= 0; i--)
- dest.unshift(source[i])
- }
- }
-
- function findMatchingMode(tagInfo, tagText) {
- for (var i = 0; i < tagInfo.length; i++) {
- var spec = tagInfo[i];
- if (!spec[0] || spec[1].test(getAttrValue(tagText, spec[0]))) return spec[2];
- }
- }
-
- CodeMirror.defineMode("htmlmixed", function (config, parserConfig) {
- var htmlMode = CodeMirror.getMode(config, {
- name: "xml",
- htmlMode: true,
- multilineTagIndentFactor: parserConfig.multilineTagIndentFactor,
- multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag
- });
-
- var tags = {};
- var configTags = parserConfig && parserConfig.tags, configScript = parserConfig && parserConfig.scriptTypes;
- addTags(defaultTags, tags);
- if (configTags) addTags(configTags, tags);
- if (configScript) for (var i = configScript.length - 1; i >= 0; i--)
- tags.script.unshift(["type", configScript[i].matches, configScript[i].mode])
-
- function html(stream, state) {
- var style = htmlMode.token(stream, state.htmlState), tag = /\btag\b/.test(style), tagName
- if (tag && !/[<>\s\/]/.test(stream.current()) &&
- (tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase()) &&
- tags.hasOwnProperty(tagName)) {
- state.inTag = tagName + " "
- } else if (state.inTag && tag && />$/.test(stream.current())) {
- var inTag = /^([\S]+) (.*)/.exec(state.inTag)
- state.inTag = null
- var modeSpec = stream.current() == ">" && findMatchingMode(tags[inTag[1]], inTag[2])
- var mode = CodeMirror.getMode(config, modeSpec)
- var endTagA = getTagRegexp(inTag[1], true), endTag = getTagRegexp(inTag[1], false);
- state.token = function (stream, state) {
- if (stream.match(endTagA, false)) {
- state.token = html;
- state.localState = state.localMode = null;
- return null;
- }
- return maybeBackup(stream, endTag, state.localMode.token(stream, state.localState));
- };
- state.localMode = mode;
- state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, ""));
- } else if (state.inTag) {
- state.inTag += stream.current()
- if (stream.eol()) state.inTag += " "
- }
- return style;
- };
-
- return {
- startState: function () {
- var state = CodeMirror.startState(htmlMode);
- return {token: html, inTag: null, localMode: null, localState: null, htmlState: state};
- },
-
- copyState: function (state) {
- var local;
- if (state.localState) {
- local = CodeMirror.copyState(state.localMode, state.localState);
- }
- return {token: state.token, inTag: state.inTag,
- localMode: state.localMode, localState: local,
- htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
- },
-
- token: function (stream, state) {
- return state.token(stream, state);
- },
-
- indent: function (state, textAfter) {
- if (!state.localMode || /^\s*<\//.test(textAfter))
- return htmlMode.indent(state.htmlState, textAfter);
- else if (state.localMode.indent)
- return state.localMode.indent(state.localState, textAfter);
- else
- return CodeMirror.Pass;
- },
-
- innerMode: function (state) {
- return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode};
- }
- };
- }, "xml", "javascript", "css");
-
- CodeMirror.defineMIME("text/html", "htmlmixed");
-});
diff --git a/client/utils/p5-hinter.js b/client/utils/p5-hinter.js
index 25d68c59f2..dee7c9870b 100644
--- a/client/utils/p5-hinter.js
+++ b/client/utils/p5-hinter.js
@@ -1,1734 +1,3 @@
/* eslint-disable */
/* generated: do not edit! helper file for hinter. generated by update-p5-hinter script */
-exports.p5Hinter = [
- {
- text: 'describe',
- type: 'fun',
- params: [
- { p: 'text', o: false },
- { p: 'display', o: true }
- ],
- p5: true
- },
- {
- text: 'describeElement',
- type: 'fun',
- params: [
- { p: 'name', o: false },
- { p: 'text', o: false },
- { p: 'display', o: true }
- ],
- p5: true
- },
- {
- text: 'textOutput',
- type: 'fun',
- params: [{ p: 'display', o: true }],
- p5: true
- },
- {
- text: 'gridOutput',
- type: 'fun',
- params: [{ p: 'display', o: true }],
- p5: true
- },
- { text: 'alpha', type: 'fun', params: [{ p: 'color', o: false }], p5: true },
- { text: 'blue', type: 'fun', params: [{ p: 'color', o: false }], p5: true },
- {
- text: 'brightness',
- type: 'fun',
- params: [{ p: 'color', o: false }],
- p5: true
- },
- { text: 'color', type: 'fun', p5: true },
- { text: 'green', type: 'fun', params: [{ p: 'color', o: false }], p5: true },
- { text: 'hue', type: 'fun', params: [{ p: 'color', o: false }], p5: true },
- {
- text: 'lerpColor',
- type: 'fun',
- params: [
- { p: 'c1', o: false },
- { p: 'c2', o: false },
- { p: 'amt', o: false }
- ],
- p5: true
- },
- {
- text: 'lightness',
- type: 'fun',
- params: [{ p: 'color', o: false }],
- p5: true
- },
- { text: 'red', type: 'fun', params: [{ p: 'color', o: false }], p5: true },
- {
- text: 'saturation',
- type: 'fun',
- params: [{ p: 'color', o: false }],
- p5: true
- },
- {
- text: 'beginClip',
- type: 'fun',
- params: [{ p: 'options', o: true }],
- p5: true
- },
- { text: 'endClip', type: 'fun', p5: true },
- {
- text: 'clip',
- type: 'fun',
- params: [
- { p: 'callback', o: false },
- { p: 'options', o: true }
- ],
- p5: true
- },
- { text: 'background', type: 'fun', p5: true },
- {
- text: 'clear',
- type: 'fun',
- params: [
- { p: 'r', o: true },
- { p: 'g', o: true },
- { p: 'b', o: true },
- { p: 'a', o: true }
- ],
- p5: true
- },
- { text: 'colorMode', type: 'fun', p5: true },
- { text: 'fill', type: 'fun', p5: true },
- { text: 'noFill', type: 'fun', p5: true },
- { text: 'noStroke', type: 'fun', p5: true },
- { text: 'stroke', type: 'fun', p5: true },
- {
- text: 'erase',
- type: 'fun',
- params: [
- { p: 'strengthFill', o: true },
- { p: 'strengthStroke', o: true }
- ],
- p5: true
- },
- { text: 'noErase', type: 'fun', p5: true },
- {
- text: 'arc',
- type: 'fun',
- params: [
- { p: 'x', o: false },
- { p: 'y', o: false },
- { p: 'w', o: false },
- { p: 'h', o: false },
- { p: 'start', o: false },
- { p: 'stop', o: false },
- { p: 'mode', o: true },
- { p: 'detail', o: true }
- ],
- p5: true
- },
- { text: 'ellipse', type: 'fun', p5: true },
- {
- text: 'circle',
- type: 'fun',
- params: [
- { p: 'x', o: false },
- { p: 'y', o: false },
- { p: 'd', o: false }
- ],
- p5: true
- },
- { text: 'line', type: 'fun', p5: true },
- { text: 'point', type: 'fun', p5: true },
- { text: 'quad', type: 'fun', p5: true },
- { text: 'rect', type: 'fun', p5: true },
- {
- text: 'square',
- type: 'fun',
- params: [
- { p: 'x', o: false },
- { p: 'y', o: false },
- { p: 's', o: false },
- { p: 'tl', o: true },
- { p: 'tr', o: true },
- { p: 'br', o: true },
- { p: 'bl', o: true }
- ],
- p5: true
- },
- {
- text: 'triangle',
- type: 'fun',
- params: [
- { p: 'x1', o: false },
- { p: 'y1', o: false },
- { p: 'x2', o: false },
- { p: 'y2', o: false },
- { p: 'x3', o: false },
- { p: 'y3', o: false }
- ],
- p5: true
- },
- {
- text: 'ellipseMode',
- type: 'fun',
- params: [{ p: 'mode', o: false }],
- p5: true
- },
- { text: 'noSmooth', type: 'fun', p5: true },
- {
- text: 'rectMode',
- type: 'fun',
- params: [{ p: 'mode', o: false }],
- p5: true
- },
- { text: 'smooth', type: 'fun', p5: true },
- {
- text: 'strokeCap',
- type: 'fun',
- params: [{ p: 'cap', o: false }],
- p5: true
- },
- {
- text: 'strokeJoin',
- type: 'fun',
- params: [{ p: 'join', o: false }],
- p5: true
- },
- {
- text: 'strokeWeight',
- type: 'fun',
- params: [{ p: 'weight', o: false }],
- p5: true
- },
- { text: 'bezier', type: 'fun', p5: true },
- {
- text: 'bezierDetail',
- type: 'fun',
- params: [{ p: 'detail', o: false }],
- p5: true
- },
- {
- text: 'bezierPoint',
- type: 'fun',
- params: [
- { p: 'a', o: false },
- { p: 'b', o: false },
- { p: 'c', o: false },
- { p: 'd', o: false },
- { p: 't', o: false }
- ],
- p5: true
- },
- {
- text: 'bezierTangent',
- type: 'fun',
- params: [
- { p: 'a', o: false },
- { p: 'b', o: false },
- { p: 'c', o: false },
- { p: 'd', o: false },
- { p: 't', o: false }
- ],
- p5: true
- },
- { text: 'curve', type: 'fun', p5: true },
- {
- text: 'curveDetail',
- type: 'fun',
- params: [{ p: 'resolution', o: false }],
- p5: true
- },
- {
- text: 'curveTightness',
- type: 'fun',
- params: [{ p: 'amount', o: false }],
- p5: true
- },
- {
- text: 'curvePoint',
- type: 'fun',
- params: [
- { p: 'a', o: false },
- { p: 'b', o: false },
- { p: 'c', o: false },
- { p: 'd', o: false },
- { p: 't', o: false }
- ],
- p5: true
- },
- {
- text: 'curveTangent',
- type: 'fun',
- params: [
- { p: 'a', o: false },
- { p: 'b', o: false },
- { p: 'c', o: false },
- { p: 'd', o: false },
- { p: 't', o: false }
- ],
- p5: true
- },
- { text: 'beginContour', type: 'fun', p5: true },
- {
- text: 'beginShape',
- type: 'fun',
- params: [{ p: 'kind', o: true }],
- p5: true
- },
- { text: 'bezierVertex', type: 'fun', p5: true },
- { text: 'curveVertex', type: 'fun', p5: true },
- { text: 'endContour', type: 'fun', p5: true },
- {
- text: 'endShape',
- type: 'fun',
- params: [
- { p: 'mode', o: true },
- { p: 'count', o: true }
- ],
- p5: true
- },
- { text: 'quadraticVertex', type: 'fun', p5: true },
- { text: 'vertex', type: 'fun', p5: true },
- { text: 'normal', type: 'fun', p5: true },
- { text: 'VERSION', type: 'var', params: [], p5: true },
- { text: 'P2D', type: 'var', params: [], p5: true },
- { text: 'WEBGL', type: 'var', params: [], p5: true },
- { text: 'WEBGL2', type: 'var', params: [], p5: true },
- { text: 'ARROW', type: 'var', params: [], p5: true },
- { text: 'CROSS', type: 'var', params: [], p5: true },
- { text: 'HAND', type: 'var', params: [], p5: true },
- { text: 'MOVE', type: 'var', params: [], p5: true },
- { text: 'TEXT', type: 'var', params: [], p5: true },
- { text: 'WAIT', type: 'var', params: [], p5: true },
- { text: 'HALF_PI', type: 'var', params: [], p5: true },
- { text: 'PI', type: 'var', params: [], p5: true },
- { text: 'QUARTER_PI', type: 'var', params: [], p5: true },
- { text: 'TAU', type: 'var', params: [], p5: true },
- { text: 'TWO_PI', type: 'var', params: [], p5: true },
- { text: 'DEGREES', type: 'var', params: [], p5: true },
- { text: 'RADIANS', type: 'var', params: [], p5: true },
- { text: 'CORNER', type: 'var', params: [], p5: true },
- { text: 'CORNERS', type: 'var', params: [], p5: true },
- { text: 'RADIUS', type: 'var', params: [], p5: true },
- { text: 'RIGHT', type: 'var', params: [], p5: true },
- { text: 'LEFT', type: 'var', params: [], p5: true },
- { text: 'CENTER', type: 'var', params: [], p5: true },
- { text: 'TOP', type: 'var', params: [], p5: true },
- { text: 'BOTTOM', type: 'var', params: [], p5: true },
- { text: 'BASELINE', type: 'var', params: [], p5: true },
- { text: 'POINTS', type: 'var', params: [], p5: true },
- { text: 'LINES', type: 'var', params: [], p5: true },
- { text: 'LINE_STRIP', type: 'var', params: [], p5: true },
- { text: 'LINE_LOOP', type: 'var', params: [], p5: true },
- { text: 'TRIANGLES', type: 'var', params: [], p5: true },
- { text: 'TRIANGLE_FAN', type: 'var', params: [], p5: true },
- { text: 'TRIANGLE_STRIP', type: 'var', params: [], p5: true },
- { text: 'QUADS', type: 'var', params: [], p5: true },
- { text: 'QUAD_STRIP', type: 'var', params: [], p5: true },
- { text: 'TESS', type: 'var', params: [], p5: true },
- { text: 'CLOSE', type: 'var', params: [], p5: true },
- { text: 'OPEN', type: 'var', params: [], p5: true },
- { text: 'CHORD', type: 'var', params: [], p5: true },
- { text: 'PIE', type: 'var', params: [], p5: true },
- { text: 'PROJECT', type: 'var', params: [], p5: true },
- { text: 'SQUARE', type: 'var', params: [], p5: true },
- { text: 'ROUND', type: 'var', params: [], p5: true },
- { text: 'BEVEL', type: 'var', params: [], p5: true },
- { text: 'MITER', type: 'var', params: [], p5: true },
- { text: 'RGB', type: 'var', params: [], p5: true },
- { text: 'HSB', type: 'var', params: [], p5: true },
- { text: 'HSL', type: 'var', params: [], p5: true },
- { text: 'AUTO', type: 'var', params: [], p5: true },
- { text: 'ALT', type: 'var', params: [], p5: true },
- { text: 'BACKSPACE', type: 'var', params: [], p5: true },
- { text: 'CONTROL', type: 'var', params: [], p5: true },
- { text: 'DELETE', type: 'var', params: [], p5: true },
- { text: 'DOWN_ARROW', type: 'var', params: [], p5: true },
- { text: 'ENTER', type: 'var', params: [], p5: true },
- { text: 'ESCAPE', type: 'var', params: [], p5: true },
- { text: 'LEFT_ARROW', type: 'var', params: [], p5: true },
- { text: 'OPTION', type: 'var', params: [], p5: true },
- { text: 'RETURN', type: 'var', params: [], p5: true },
- { text: 'RIGHT_ARROW', type: 'var', params: [], p5: true },
- { text: 'SHIFT', type: 'var', params: [], p5: true },
- { text: 'TAB', type: 'var', params: [], p5: true },
- { text: 'UP_ARROW', type: 'var', params: [], p5: true },
- { text: 'BLEND', type: 'var', params: [], p5: true },
- { text: 'REMOVE', type: 'var', params: [], p5: true },
- { text: 'ADD', type: 'var', params: [], p5: true },
- { text: 'DARKEST', type: 'var', params: [], p5: true },
- { text: 'LIGHTEST', type: 'var', params: [], p5: true },
- { text: 'DIFFERENCE', type: 'var', params: [], p5: true },
- { text: 'SUBTRACT', type: 'var', params: [], p5: true },
- { text: 'EXCLUSION', type: 'var', params: [], p5: true },
- { text: 'MULTIPLY', type: 'var', params: [], p5: true },
- { text: 'SCREEN', type: 'var', params: [], p5: true },
- { text: 'REPLACE', type: 'var', params: [], p5: true },
- { text: 'OVERLAY', type: 'var', params: [], p5: true },
- { text: 'HARD_LIGHT', type: 'var', params: [], p5: true },
- { text: 'SOFT_LIGHT', type: 'var', params: [], p5: true },
- { text: 'DODGE', type: 'var', params: [], p5: true },
- { text: 'BURN', type: 'var', params: [], p5: true },
- { text: 'THRESHOLD', type: 'var', params: [], p5: true },
- { text: 'GRAY', type: 'var', params: [], p5: true },
- { text: 'OPAQUE', type: 'var', params: [], p5: true },
- { text: 'INVERT', type: 'var', params: [], p5: true },
- { text: 'POSTERIZE', type: 'var', params: [], p5: true },
- { text: 'DILATE', type: 'var', params: [], p5: true },
- { text: 'ERODE', type: 'var', params: [], p5: true },
- { text: 'BLUR', type: 'var', params: [], p5: true },
- { text: 'NORMAL', type: 'var', params: [], p5: true },
- { text: 'ITALIC', type: 'var', params: [], p5: true },
- { text: 'BOLD', type: 'var', params: [], p5: true },
- { text: 'BOLDITALIC', type: 'var', params: [], p5: true },
- { text: 'CHAR', type: 'var', params: [], p5: true },
- { text: 'WORD', type: 'var', params: [], p5: true },
- { text: 'LINEAR', type: 'var', params: [], p5: true },
- { text: 'QUADRATIC', type: 'var', params: [], p5: true },
- { text: 'BEZIER', type: 'var', params: [], p5: true },
- { text: 'CURVE', type: 'var', params: [], p5: true },
- { text: 'STROKE', type: 'var', params: [], p5: true },
- { text: 'FILL', type: 'var', params: [], p5: true },
- { text: 'TEXTURE', type: 'var', params: [], p5: true },
- { text: 'IMMEDIATE', type: 'var', params: [], p5: true },
- { text: 'IMAGE', type: 'var', params: [], p5: true },
- { text: 'NEAREST', type: 'var', params: [], p5: true },
- { text: 'REPEAT', type: 'var', params: [], p5: true },
- { text: 'CLAMP', type: 'var', params: [], p5: true },
- { text: 'MIRROR', type: 'var', params: [], p5: true },
- { text: 'FLAT', type: 'var', params: [], p5: true },
- { text: 'SMOOTH', type: 'var', params: [], p5: true },
- { text: 'LANDSCAPE', type: 'var', params: [], p5: true },
- { text: 'PORTRAIT', type: 'var', params: [], p5: true },
- { text: 'GRID', type: 'var', params: [], p5: true },
- { text: 'AXES', type: 'var', params: [], p5: true },
- { text: 'LABEL', type: 'var', params: [], p5: true },
- { text: 'FALLBACK', type: 'var', params: [], p5: true },
- { text: 'CONTAIN', type: 'var', params: [], p5: true },
- { text: 'COVER', type: 'var', params: [], p5: true },
- { text: 'UNSIGNED_BYTE', type: 'var', params: [], p5: true },
- { text: 'UNSIGNED_INT', type: 'var', params: [], p5: true },
- { text: 'FLOAT', type: 'var', params: [], p5: true },
- { text: 'HALF_FLOAT', type: 'var', params: [], p5: true },
- { text: 'RGBA', type: 'var', params: [], p5: true },
- {
- text: 'print',
- type: 'fun',
- params: [{ p: 'contents', o: false }],
- p5: true
- },
- { text: 'frameCount', type: 'var', params: [], p5: true },
- { text: 'deltaTime', type: 'var', params: [], p5: true },
- { text: 'focused', type: 'var', params: [], p5: true },
- {
- text: 'cursor',
- type: 'fun',
- params: [
- { p: 'type', o: false },
- { p: 'x', o: true },
- { p: 'y', o: true }
- ],
- p5: true
- },
- { text: 'frameRate', type: 'fun', p5: true },
- { text: 'getTargetFrameRate', type: 'fun', p5: true },
- { text: 'noCursor', type: 'fun', p5: true },
- { text: 'webglVersion', type: 'var', params: [], p5: true },
- { text: 'displayWidth', type: 'var', params: [], p5: true },
- { text: 'displayHeight', type: 'var', params: [], p5: true },
- { text: 'windowWidth', type: 'var', params: [], p5: true },
- { text: 'windowHeight', type: 'var', params: [], p5: true },
- {
- text: 'windowResized',
- type: 'fun',
- params: [{ p: 'event', o: true }],
- p5: true
- },
- { text: 'width', type: 'var', params: [], p5: true },
- { text: 'height', type: 'var', params: [], p5: true },
- {
- text: 'fullscreen',
- type: 'fun',
- params: [{ p: 'val', o: true }],
- p5: true
- },
- { text: 'pixelDensity', type: 'fun', p5: true },
- { text: 'displayDensity', type: 'fun', p5: true },
- { text: 'getURL', type: 'fun', p5: true },
- { text: 'getURLPath', type: 'fun', p5: true },
- { text: 'getURLParams', type: 'fun', p5: true },
- { text: 'preload', type: 'fun', p5: true },
- { text: 'setup', type: 'fun', p5: true },
- { text: 'draw', type: 'fun', p5: true },
- { text: 'remove', type: 'fun', p5: true },
- { text: 'disableFriendlyErrors', type: 'var', params: [], p5: true },
- { text: 'createCanvas', type: 'fun', p5: true },
- {
- text: 'resizeCanvas',
- type: 'fun',
- params: [
- { p: 'width', o: false },
- { p: 'height', o: false },
- { p: 'noRedraw', o: true }
- ],
- p5: true
- },
- { text: 'noCanvas', type: 'fun', p5: true },
- { text: 'createGraphics', type: 'fun', p5: true },
- {
- text: 'createFramebuffer',
- type: 'fun',
- params: [{ p: 'options', o: true }],
- p5: true
- },
- {
- text: 'clearDepth',
- type: 'fun',
- params: [{ p: 'depth', o: true }],
- p5: true
- },
- {
- text: 'blendMode',
- type: 'fun',
- params: [{ p: 'mode', o: false }],
- p5: true
- },
- { text: 'drawingContext', type: 'var', params: [], p5: true },
- { text: 'noLoop', type: 'fun', p5: true },
- { text: 'loop', type: 'fun', p5: true },
- { text: 'isLooping', type: 'fun', p5: true },
- { text: 'push', type: 'fun', p5: true },
- { text: 'pop', type: 'fun', p5: true },
- { text: 'redraw', type: 'fun', params: [{ p: 'n', o: true }], p5: true },
- {
- text: 'p5',
- type: 'fun',
- params: [
- { p: 'sketch', o: false },
- { p: 'node', o: false }
- ],
- p5: true
- },
- { text: 'applyMatrix', type: 'fun', p5: true },
- { text: 'resetMatrix', type: 'fun', p5: true },
- {
- text: 'rotate',
- type: 'fun',
- params: [
- { p: 'angle', o: false },
- { p: 'axis', o: true }
- ],
- p5: true
- },
- {
- text: 'rotateX',
- type: 'fun',
- params: [{ p: 'angle', o: false }],
- p5: true
- },
- {
- text: 'rotateY',
- type: 'fun',
- params: [{ p: 'angle', o: false }],
- p5: true
- },
- {
- text: 'rotateZ',
- type: 'fun',
- params: [{ p: 'angle', o: false }],
- p5: true
- },
- { text: 'scale', type: 'fun', p5: true },
- { text: 'shearX', type: 'fun', params: [{ p: 'angle', o: false }], p5: true },
- { text: 'shearY', type: 'fun', params: [{ p: 'angle', o: false }], p5: true },
- { text: 'translate', type: 'fun', p5: true },
- {
- text: 'storeItem',
- type: 'fun',
- params: [
- { p: 'key', o: false },
- { p: 'value', o: false }
- ],
- p5: true
- },
- { text: 'getItem', type: 'fun', params: [{ p: 'key', o: false }], p5: true },
- { text: 'clearStorage', type: 'fun', p5: true },
- {
- text: 'removeItem',
- type: 'fun',
- params: [{ p: 'key', o: false }],
- p5: true
- },
- { text: 'createStringDict', type: 'fun', p5: true },
- { text: 'createNumberDict', type: 'fun', p5: true },
- {
- text: 'select',
- type: 'fun',
- params: [
- { p: 'selectors', o: false },
- { p: 'container', o: true }
- ],
- p5: true
- },
- {
- text: 'selectAll',
- type: 'fun',
- params: [
- { p: 'selectors', o: false },
- { p: 'container', o: true }
- ],
- p5: true
- },
- { text: 'removeElements', type: 'fun', p5: true },
- { text: 'changed', type: 'fun', params: [{ p: 'fxn', o: false }], p5: true },
- { text: 'input', type: 'fun', params: [{ p: 'fxn', o: false }], p5: true },
- {
- text: 'createDiv',
- type: 'fun',
- params: [{ p: 'html', o: true }],
- p5: true
- },
- { text: 'createP', type: 'fun', params: [{ p: 'html', o: true }], p5: true },
- {
- text: 'createSpan',
- type: 'fun',
- params: [{ p: 'html', o: true }],
- p5: true
- },
- { text: 'createImg', type: 'fun', p5: true },
- {
- text: 'createA',
- type: 'fun',
- params: [
- { p: 'href', o: false },
- { p: 'html', o: false },
- { p: 'target', o: true }
- ],
- p5: true
- },
- {
- text: 'createSlider',
- type: 'fun',
- params: [
- { p: 'min', o: false },
- { p: 'max', o: false },
- { p: 'value', o: true },
- { p: 'step', o: true }
- ],
- p5: true
- },
- {
- text: 'createButton',
- type: 'fun',
- params: [
- { p: 'label', o: false },
- { p: 'value', o: true }
- ],
- p5: true
- },
- {
- text: 'createCheckbox',
- type: 'fun',
- params: [
- { p: 'label', o: true },
- { p: 'value', o: true }
- ],
- p5: true
- },
- { text: 'createSelect', type: 'fun', p5: true },
- { text: 'createRadio', type: 'fun', p5: true },
- {
- text: 'createColorPicker',
- type: 'fun',
- params: [{ p: 'value', o: true }],
- p5: true
- },
- { text: 'createInput', type: 'fun', p5: true },
- {
- text: 'createFileInput',
- type: 'fun',
- params: [
- { p: 'callback', o: false },
- { p: 'multiple', o: true }
- ],
- p5: true
- },
- {
- text: 'createVideo',
- type: 'fun',
- params: [
- { p: 'src', o: false },
- { p: 'callback', o: true }
- ],
- p5: true
- },
- {
- text: 'createAudio',
- type: 'fun',
- params: [
- { p: 'src', o: true },
- { p: 'callback', o: true }
- ],
- p5: true
- },
- {
- text: 'createCapture',
- type: 'fun',
- params: [
- { p: 'type', o: true },
- { p: 'flipped', o: true },
- { p: 'callback', o: true }
- ],
- p5: true
- },
- {
- text: 'createElement',
- type: 'fun',
- params: [
- { p: 'tag', o: false },
- { p: 'content', o: true }
- ],
- p5: true
- },
- { text: 'deviceOrientation', type: 'var', params: [], p5: true },
- { text: 'accelerationX', type: 'var', params: [], p5: true },
- { text: 'accelerationY', type: 'var', params: [], p5: true },
- { text: 'accelerationZ', type: 'var', params: [], p5: true },
- { text: 'pAccelerationX', type: 'var', params: [], p5: true },
- { text: 'pAccelerationY', type: 'var', params: [], p5: true },
- { text: 'pAccelerationZ', type: 'var', params: [], p5: true },
- { text: 'rotationX', type: 'var', params: [], p5: true },
- { text: 'rotationY', type: 'var', params: [], p5: true },
- { text: 'rotationZ', type: 'var', params: [], p5: true },
- { text: 'pRotationX', type: 'var', params: [], p5: true },
- { text: 'pRotationY', type: 'var', params: [], p5: true },
- { text: 'pRotationZ', type: 'var', params: [], p5: true },
- { text: 'turnAxis', type: 'var', params: [], p5: true },
- {
- text: 'setMoveThreshold',
- type: 'fun',
- params: [{ p: 'value', o: false }],
- p5: true
- },
- {
- text: 'setShakeThreshold',
- type: 'fun',
- params: [{ p: 'value', o: false }],
- p5: true
- },
- { text: 'deviceMoved', type: 'fun', p5: true },
- { text: 'deviceTurned', type: 'fun', p5: true },
- { text: 'deviceShaken', type: 'fun', p5: true },
- { text: 'keyIsPressed', type: 'var', params: [], p5: true },
- { text: 'key', type: 'var', params: [], p5: true },
- { text: 'keyCode', type: 'var', params: [], p5: true },
- {
- text: 'keyPressed',
- type: 'fun',
- params: [{ p: 'event', o: true }],
- p5: true
- },
- {
- text: 'keyReleased',
- type: 'fun',
- params: [{ p: 'event', o: true }],
- p5: true
- },
- {
- text: 'keyTyped',
- type: 'fun',
- params: [{ p: 'event', o: true }],
- p5: true
- },
- {
- text: 'keyIsDown',
- type: 'fun',
- params: [{ p: 'code', o: false }],
- p5: true
- },
- { text: 'movedX', type: 'var', params: [], p5: true },
- { text: 'movedY', type: 'var', params: [], p5: true },
- { text: 'mouseX', type: 'var', params: [], p5: true },
- { text: 'mouseY', type: 'var', params: [], p5: true },
- { text: 'pmouseX', type: 'var', params: [], p5: true },
- { text: 'pmouseY', type: 'var', params: [], p5: true },
- { text: 'winMouseX', type: 'var', params: [], p5: true },
- { text: 'winMouseY', type: 'var', params: [], p5: true },
- { text: 'pwinMouseX', type: 'var', params: [], p5: true },
- { text: 'pwinMouseY', type: 'var', params: [], p5: true },
- { text: 'mouseButton', type: 'var', params: [], p5: true },
- { text: 'mouseIsPressed', type: 'var', params: [], p5: true },
- {
- text: 'mouseMoved',
- type: 'fun',
- params: [{ p: 'event', o: true }],
- p5: true
- },
- {
- text: 'mouseDragged',
- type: 'fun',
- params: [{ p: 'event', o: true }],
- p5: true
- },
- {
- text: 'mousePressed',
- type: 'fun',
- params: [{ p: 'event', o: true }],
- p5: true
- },
- {
- text: 'mouseReleased',
- type: 'fun',
- params: [{ p: 'event', o: true }],
- p5: true
- },
- {
- text: 'mouseClicked',
- type: 'fun',
- params: [{ p: 'event', o: true }],
- p5: true
- },
- {
- text: 'doubleClicked',
- type: 'fun',
- params: [{ p: 'event', o: true }],
- p5: true
- },
- {
- text: 'mouseWheel',
- type: 'fun',
- params: [{ p: 'event', o: true }],
- p5: true
- },
- { text: 'requestPointerLock', type: 'fun', p5: true },
- { text: 'exitPointerLock', type: 'fun', p5: true },
- { text: 'touches', type: 'var', params: [], p5: true },
- {
- text: 'touchStarted',
- type: 'fun',
- params: [{ p: 'event', o: true }],
- p5: true
- },
- {
- text: 'touchMoved',
- type: 'fun',
- params: [{ p: 'event', o: true }],
- p5: true
- },
- {
- text: 'touchEnded',
- type: 'fun',
- params: [{ p: 'event', o: true }],
- p5: true
- },
- {
- text: 'createImage',
- type: 'fun',
- params: [
- { p: 'width', o: false },
- { p: 'height', o: false }
- ],
- p5: true
- },
- { text: 'saveCanvas', type: 'fun', p5: true },
- {
- text: 'saveFrames',
- type: 'fun',
- params: [
- { p: 'filename', o: false },
- { p: 'extension', o: false },
- { p: 'duration', o: false },
- { p: 'framerate', o: false },
- { p: 'callback', o: true }
- ],
- p5: true
- },
- {
- text: 'loadImage',
- type: 'fun',
- params: [
- { p: 'path', o: false },
- { p: 'successCallback', o: true },
- { p: 'failureCallback', o: true }
- ],
- p5: true
- },
- {
- text: 'saveGif',
- type: 'fun',
- params: [
- { p: 'filename', o: false },
- { p: 'duration', o: false },
- { p: 'options', o: true }
- ],
- p5: true
- },
- { text: 'image', type: 'fun', p5: true },
- { text: 'tint', type: 'fun', p5: true },
- { text: 'noTint', type: 'fun', p5: true },
- {
- text: 'imageMode',
- type: 'fun',
- params: [{ p: 'mode', o: false }],
- p5: true
- },
- { text: 'pixels', type: 'var', params: [], p5: true },
- { text: 'blend', type: 'fun', p5: true },
- { text: 'copy', type: 'fun', p5: true },
- { text: 'filter', type: 'fun', p5: true },
- { text: 'get', type: 'fun', p5: true },
- { text: 'loadPixels', type: 'fun', p5: true },
- {
- text: 'set',
- type: 'fun',
- params: [
- { p: 'x', o: false },
- { p: 'y', o: false },
- { p: 'c', o: false }
- ],
- p5: true
- },
- {
- text: 'updatePixels',
- type: 'fun',
- params: [
- { p: 'x', o: true },
- { p: 'y', o: true },
- { p: 'w', o: true },
- { p: 'h', o: true }
- ],
- p5: true
- },
- {
- text: 'loadJSON',
- type: 'fun',
- params: [
- { p: 'path', o: false },
- { p: 'successCallback', o: true },
- { p: 'errorCallback', o: true }
- ],
- p5: true
- },
- {
- text: 'loadStrings',
- type: 'fun',
- params: [
- { p: 'path', o: false },
- { p: 'successCallback', o: true },
- { p: 'errorCallback', o: true }
- ],
- p5: true
- },
- {
- text: 'loadTable',
- type: 'fun',
- params: [
- { p: 'filename', o: false },
- { p: 'extension', o: true },
- { p: 'header', o: true },
- { p: 'callback', o: true },
- { p: 'errorCallback', o: true }
- ],
- p5: true
- },
- {
- text: 'loadXML',
- type: 'fun',
- params: [
- { p: 'path', o: false },
- { p: 'successCallback', o: true },
- { p: 'errorCallback', o: true }
- ],
- p5: true
- },
- {
- text: 'loadBytes',
- type: 'fun',
- params: [
- { p: 'file', o: false },
- { p: 'callback', o: true },
- { p: 'errorCallback', o: true }
- ],
- p5: true
- },
- { text: 'httpGet', type: 'fun', p5: true },
- { text: 'httpPost', type: 'fun', p5: true },
- { text: 'httpDo', type: 'fun', p5: true },
- {
- text: 'createWriter',
- type: 'fun',
- params: [
- { p: 'name', o: false },
- { p: 'extension', o: true }
- ],
- p5: true
- },
- {
- text: 'save',
- type: 'fun',
- params: [
- { p: 'objectOrFilename', o: true },
- { p: 'filename', o: true },
- { p: 'options', o: true }
- ],
- p5: true
- },
- {
- text: 'saveJSON',
- type: 'fun',
- params: [
- { p: 'json', o: false },
- { p: 'filename', o: false },
- { p: 'optimize', o: true }
- ],
- p5: true
- },
- {
- text: 'saveStrings',
- type: 'fun',
- params: [
- { p: 'list', o: false },
- { p: 'filename', o: false },
- { p: 'extension', o: true },
- { p: 'isCRLF', o: true }
- ],
- p5: true
- },
- {
- text: 'saveTable',
- type: 'fun',
- params: [
- { p: 'Table', o: false },
- { p: 'filename', o: false },
- { p: 'options', o: true }
- ],
- p5: true
- },
- { text: 'abs', type: 'fun', params: [{ p: 'n', o: false }], p5: true },
- { text: 'ceil', type: 'fun', params: [{ p: 'n', o: false }], p5: true },
- {
- text: 'constrain',
- type: 'fun',
- params: [
- { p: 'n', o: false },
- { p: 'low', o: false },
- { p: 'high', o: false }
- ],
- p5: true
- },
- { text: 'dist', type: 'fun', p5: true },
- { text: 'exp', type: 'fun', params: [{ p: 'n', o: false }], p5: true },
- { text: 'floor', type: 'fun', params: [{ p: 'n', o: false }], p5: true },
- {
- text: 'lerp',
- type: 'fun',
- params: [
- { p: 'start', o: false },
- { p: 'stop', o: false },
- { p: 'amt', o: false }
- ],
- p5: true
- },
- { text: 'log', type: 'fun', params: [{ p: 'n', o: false }], p5: true },
- {
- text: 'mag',
- type: 'fun',
- params: [
- { p: 'x', o: false },
- { p: 'y', o: false }
- ],
- p5: true
- },
- {
- text: 'map',
- type: 'fun',
- params: [
- { p: 'value', o: false },
- { p: 'start1', o: false },
- { p: 'stop1', o: false },
- { p: 'start2', o: false },
- { p: 'stop2', o: false },
- { p: 'withinBounds', o: true }
- ],
- p5: true
- },
- { text: 'max', type: 'fun', p5: true },
- { text: 'min', type: 'fun', p5: true },
- {
- text: 'norm',
- type: 'fun',
- params: [
- { p: 'value', o: false },
- { p: 'start', o: false },
- { p: 'stop', o: false }
- ],
- p5: true
- },
- {
- text: 'pow',
- type: 'fun',
- params: [
- { p: 'n', o: false },
- { p: 'e', o: false }
- ],
- p5: true
- },
- {
- text: 'round',
- type: 'fun',
- params: [
- { p: 'n', o: false },
- { p: 'decimals', o: true }
- ],
- p5: true
- },
- { text: 'sq', type: 'fun', params: [{ p: 'n', o: false }], p5: true },
- { text: 'sqrt', type: 'fun', params: [{ p: 'n', o: false }], p5: true },
- { text: 'fract', type: 'fun', params: [{ p: 'n', o: false }], p5: true },
- {
- text: 'createVector',
- type: 'fun',
- params: [
- { p: 'x', o: true },
- { p: 'y', o: true },
- { p: 'z', o: true }
- ],
- p5: true
- },
- {
- text: 'noise',
- type: 'fun',
- params: [
- { p: 'x', o: false },
- { p: 'y', o: true },
- { p: 'z', o: true }
- ],
- p5: true
- },
- {
- text: 'noiseDetail',
- type: 'fun',
- params: [
- { p: 'lod', o: false },
- { p: 'falloff', o: false }
- ],
- p5: true
- },
- {
- text: 'noiseSeed',
- type: 'fun',
- params: [{ p: 'seed', o: false }],
- p5: true
- },
- {
- text: 'randomSeed',
- type: 'fun',
- params: [{ p: 'seed', o: false }],
- p5: true
- },
- { text: 'random', type: 'fun', p5: true },
- {
- text: 'randomGaussian',
- type: 'fun',
- params: [
- { p: 'mean', o: true },
- { p: 'sd', o: true }
- ],
- p5: true
- },
- { text: 'acos', type: 'fun', params: [{ p: 'value', o: false }], p5: true },
- { text: 'asin', type: 'fun', params: [{ p: 'value', o: false }], p5: true },
- { text: 'atan', type: 'fun', params: [{ p: 'value', o: false }], p5: true },
- {
- text: 'atan2',
- type: 'fun',
- params: [
- { p: 'y', o: false },
- { p: 'x', o: false }
- ],
- p5: true
- },
- { text: 'cos', type: 'fun', params: [{ p: 'angle', o: false }], p5: true },
- { text: 'sin', type: 'fun', params: [{ p: 'angle', o: false }], p5: true },
- { text: 'tan', type: 'fun', params: [{ p: 'angle', o: false }], p5: true },
- {
- text: 'degrees',
- type: 'fun',
- params: [{ p: 'radians', o: false }],
- p5: true
- },
- {
- text: 'radians',
- type: 'fun',
- params: [{ p: 'degrees', o: false }],
- p5: true
- },
- { text: 'angleMode', type: 'fun', p5: true },
- { text: 'textAlign', type: 'fun', p5: true },
- { text: 'textLeading', type: 'fun', p5: true },
- { text: 'textSize', type: 'fun', p5: true },
- { text: 'textStyle', type: 'fun', p5: true },
- {
- text: 'textWidth',
- type: 'fun',
- params: [{ p: 'str', o: false }],
- p5: true
- },
- { text: 'textAscent', type: 'fun', p5: true },
- { text: 'textDescent', type: 'fun', p5: true },
- {
- text: 'textWrap',
- type: 'fun',
- params: [{ p: 'style', o: false }],
- p5: true
- },
- {
- text: 'loadFont',
- type: 'fun',
- params: [
- { p: 'path', o: false },
- { p: 'successCallback', o: true },
- { p: 'failureCallback', o: true }
- ],
- p5: true
- },
- {
- text: 'text',
- type: 'fun',
- params: [
- { p: 'str', o: false },
- { p: 'x', o: false },
- { p: 'y', o: false },
- { p: 'maxWidth', o: true },
- { p: 'maxHeight', o: true }
- ],
- p5: true
- },
- { text: 'textFont', type: 'fun', p5: true },
- {
- text: 'append',
- type: 'fun',
- params: [
- { p: 'array', o: false },
- { p: 'value', o: false }
- ],
- p5: true
- },
- { text: 'arrayCopy', type: 'fun', p5: true },
- {
- text: 'concat',
- type: 'fun',
- params: [
- { p: 'a', o: false },
- { p: 'b', o: false }
- ],
- p5: true
- },
- { text: 'reverse', type: 'fun', params: [{ p: 'list', o: false }], p5: true },
- { text: 'shorten', type: 'fun', params: [{ p: 'list', o: false }], p5: true },
- {
- text: 'shuffle',
- type: 'fun',
- params: [
- { p: 'array', o: false },
- { p: 'bool', o: true }
- ],
- p5: true
- },
- {
- text: 'sort',
- type: 'fun',
- params: [
- { p: 'list', o: false },
- { p: 'count', o: true }
- ],
- p5: true
- },
- {
- text: 'splice',
- type: 'fun',
- params: [
- { p: 'list', o: false },
- { p: 'value', o: false },
- { p: 'position', o: false }
- ],
- p5: true
- },
- {
- text: 'subset',
- type: 'fun',
- params: [
- { p: 'list', o: false },
- { p: 'start', o: false },
- { p: 'count', o: true }
- ],
- p5: true
- },
- { text: 'float', type: 'fun', p5: true },
- { text: 'int', type: 'fun', p5: true },
- { text: 'str', type: 'fun', params: [{ p: 'n', o: false }], p5: true },
- { text: 'boolean', type: 'fun', p5: true },
- { text: 'byte', type: 'fun', p5: true },
- { text: 'char', type: 'fun', p5: true },
- { text: 'unchar', type: 'fun', p5: true },
- { text: 'hex', type: 'fun', p5: true },
- { text: 'unhex', type: 'fun', p5: true },
- {
- text: 'join',
- type: 'fun',
- params: [
- { p: 'list', o: false },
- { p: 'separator', o: false }
- ],
- p5: true
- },
- {
- text: 'match',
- type: 'fun',
- params: [
- { p: 'str', o: false },
- { p: 'regexp', o: false }
- ],
- p5: true
- },
- {
- text: 'matchAll',
- type: 'fun',
- params: [
- { p: 'str', o: false },
- { p: 'regexp', o: false }
- ],
- p5: true
- },
- { text: 'nf', type: 'fun', p5: true },
- { text: 'nfc', type: 'fun', p5: true },
- { text: 'nfp', type: 'fun', p5: true },
- { text: 'nfs', type: 'fun', p5: true },
- {
- text: 'split',
- type: 'fun',
- params: [
- { p: 'value', o: false },
- { p: 'delim', o: false }
- ],
- p5: true
- },
- {
- text: 'splitTokens',
- type: 'fun',
- params: [
- { p: 'value', o: false },
- { p: 'delim', o: true }
- ],
- p5: true
- },
- { text: 'trim', type: 'fun', p5: true },
- { text: 'day', type: 'fun', p5: true },
- { text: 'hour', type: 'fun', p5: true },
- { text: 'minute', type: 'fun', p5: true },
- { text: 'millis', type: 'fun', p5: true },
- { text: 'month', type: 'fun', p5: true },
- { text: 'second', type: 'fun', p5: true },
- { text: 'year', type: 'fun', p5: true },
- { text: 'beginGeometry', type: 'fun', p5: true },
- { text: 'endGeometry', type: 'fun', p5: true },
- {
- text: 'buildGeometry',
- type: 'fun',
- params: [{ p: 'callback', o: false }],
- p5: true
- },
- {
- text: 'freeGeometry',
- type: 'fun',
- params: [{ p: 'geometry', o: false }],
- p5: true
- },
- {
- text: 'plane',
- type: 'fun',
- params: [
- { p: 'width', o: true },
- { p: 'height', o: true },
- { p: 'detailX', o: true },
- { p: 'detailY', o: true }
- ],
- p5: true
- },
- {
- text: 'box',
- type: 'fun',
- params: [
- { p: 'width', o: true },
- { p: 'height', o: true },
- { p: 'depth', o: true },
- { p: 'detailX', o: true },
- { p: 'detailY', o: true }
- ],
- p5: true
- },
- {
- text: 'sphere',
- type: 'fun',
- params: [
- { p: 'radius', o: true },
- { p: 'detailX', o: true },
- { p: 'detailY', o: true }
- ],
- p5: true
- },
- {
- text: 'cylinder',
- type: 'fun',
- params: [
- { p: 'radius', o: true },
- { p: 'height', o: true },
- { p: 'detailX', o: true },
- { p: 'detailY', o: true },
- { p: 'bottomCap', o: true },
- { p: 'topCap', o: true }
- ],
- p5: true
- },
- {
- text: 'cone',
- type: 'fun',
- params: [
- { p: 'radius', o: true },
- { p: 'height', o: true },
- { p: 'detailX', o: true },
- { p: 'detailY', o: true },
- { p: 'cap', o: true }
- ],
- p5: true
- },
- {
- text: 'ellipsoid',
- type: 'fun',
- params: [
- { p: 'radiusX', o: true },
- { p: 'radiusY', o: true },
- { p: 'radiusZ', o: true },
- { p: 'detailX', o: true },
- { p: 'detailY', o: true }
- ],
- p5: true
- },
- {
- text: 'torus',
- type: 'fun',
- params: [
- { p: 'radius', o: true },
- { p: 'tubeRadius', o: true },
- { p: 'detailX', o: true },
- { p: 'detailY', o: true }
- ],
- p5: true
- },
- {
- text: 'orbitControl',
- type: 'fun',
- params: [
- { p: 'sensitivityX', o: true },
- { p: 'sensitivityY', o: true },
- { p: 'sensitivityZ', o: true },
- { p: 'options', o: true }
- ],
- p5: true
- },
- { text: 'debugMode', type: 'fun', p5: true },
- { text: 'noDebugMode', type: 'fun', p5: true },
- { text: 'ambientLight', type: 'fun', p5: true },
- { text: 'specularColor', type: 'fun', p5: true },
- { text: 'directionalLight', type: 'fun', p5: true },
- { text: 'pointLight', type: 'fun', p5: true },
- {
- text: 'imageLight',
- type: 'fun',
- params: [{ p: 'img', o: false }],
- p5: true
- },
- { text: 'panorama', type: 'fun', params: [{ p: 'img', o: false }], p5: true },
- { text: 'lights', type: 'fun', p5: true },
- {
- text: 'lightFalloff',
- type: 'fun',
- params: [
- { p: 'constant', o: false },
- { p: 'linear', o: false },
- { p: 'quadratic', o: false }
- ],
- p5: true
- },
- { text: 'spotLight', type: 'fun', p5: true },
- { text: 'noLights', type: 'fun', p5: true },
- { text: 'loadModel', type: 'fun', p5: true },
- { text: 'model', type: 'fun', params: [{ p: 'model', o: false }], p5: true },
- {
- text: 'loadShader',
- type: 'fun',
- params: [
- { p: 'vertFilename', o: false },
- { p: 'fragFilename', o: false },
- { p: 'successCallback', o: true },
- { p: 'failureCallback', o: true }
- ],
- p5: true
- },
- {
- text: 'createShader',
- type: 'fun',
- params: [
- { p: 'vertSrc', o: false },
- { p: 'fragSrc', o: false }
- ],
- p5: true
- },
- {
- text: 'createFilterShader',
- type: 'fun',
- params: [{ p: 'fragSrc', o: false }],
- p5: true
- },
- { text: 'shader', type: 'fun', params: [{ p: 's', o: false }], p5: true },
- { text: 'resetShader', type: 'fun', p5: true },
- { text: 'texture', type: 'fun', params: [{ p: 'tex', o: false }], p5: true },
- {
- text: 'textureMode',
- type: 'fun',
- params: [{ p: 'mode', o: false }],
- p5: true
- },
- {
- text: 'textureWrap',
- type: 'fun',
- params: [
- { p: 'wrapX', o: false },
- { p: 'wrapY', o: true }
- ],
- p5: true
- },
- { text: 'normalMaterial', type: 'fun', p5: true },
- { text: 'ambientMaterial', type: 'fun', p5: true },
- { text: 'emissiveMaterial', type: 'fun', p5: true },
- { text: 'specularMaterial', type: 'fun', p5: true },
- {
- text: 'shininess',
- type: 'fun',
- params: [{ p: 'shine', o: false }],
- p5: true
- },
- {
- text: 'metalness',
- type: 'fun',
- params: [{ p: 'metallic', o: false }],
- p5: true
- },
- {
- text: 'camera',
- type: 'fun',
- params: [
- { p: 'x', o: true },
- { p: 'y', o: true },
- { p: 'z', o: true },
- { p: 'centerX', o: true },
- { p: 'centerY', o: true },
- { p: 'centerZ', o: true },
- { p: 'upX', o: true },
- { p: 'upY', o: true },
- { p: 'upZ', o: true }
- ],
- p5: true
- },
- {
- text: 'perspective',
- type: 'fun',
- params: [
- { p: 'fovy', o: true },
- { p: 'aspect', o: true },
- { p: 'near', o: true },
- { p: 'far', o: true }
- ],
- p5: true
- },
- { text: 'linePerspective', type: 'fun', p5: true },
- {
- text: 'ortho',
- type: 'fun',
- params: [
- { p: 'left', o: true },
- { p: 'right', o: true },
- { p: 'bottom', o: true },
- { p: 'top', o: true },
- { p: 'near', o: true },
- { p: 'far', o: true }
- ],
- p5: true
- },
- {
- text: 'frustum',
- type: 'fun',
- params: [
- { p: 'left', o: true },
- { p: 'right', o: true },
- { p: 'bottom', o: true },
- { p: 'top', o: true },
- { p: 'near', o: true },
- { p: 'far', o: true }
- ],
- p5: true
- },
- { text: 'createCamera', type: 'fun', p5: true },
- {
- text: 'setCamera',
- type: 'fun',
- params: [{ p: 'cam', o: false }],
- p5: true
- },
- { text: 'setAttributes', type: 'fun', p5: true },
- { text: 'getAudioContext', type: 'fun', p5: true },
- {
- text: 'userStartAudio',
- type: 'fun',
- params: [
- { p: 'elements', o: true },
- { p: 'callback', o: true }
- ],
- p5: true
- },
- { text: 'getOutputVolume', type: 'fun', p5: true },
- {
- text: 'outputVolume',
- type: 'fun',
- params: [
- { p: 'volume', o: false },
- { p: 'rampTime', o: true },
- { p: 'timeFromNow', o: true }
- ],
- p5: true
- },
- { text: 'soundOut', type: 'var', params: [], p5: true },
- { text: 'sampleRate', type: 'fun', p5: true },
- {
- text: 'freqToMidi',
- type: 'fun',
- params: [{ p: 'frequency', o: false }],
- p5: true
- },
- {
- text: 'midiToFreq',
- type: 'fun',
- params: [{ p: 'midiNote', o: false }],
- p5: true
- },
- {
- text: 'soundFormats',
- type: 'fun',
- params: [{ p: 'formats', o: true }],
- p5: true
- },
- {
- text: 'saveSound',
- type: 'fun',
- params: [
- { p: 'soundFile', o: false },
- { p: 'fileName', o: false }
- ],
- p5: true
- },
- {
- text: 'loadSound',
- type: 'fun',
- params: [
- { p: 'path', o: false },
- { p: 'successCallback', o: true },
- { p: 'errorCallback', o: true },
- { p: 'whileLoading', o: true }
- ],
- p5: true
- },
- {
- text: 'createConvolver',
- type: 'fun',
- params: [
- { p: 'path', o: false },
- { p: 'callback', o: true },
- { p: 'errorCallback', o: true }
- ],
- p5: true
- },
- {
- text: 'setBPM',
- type: 'fun',
- params: [
- { p: 'BPM', o: false },
- { p: 'rampTime', o: false }
- ],
- p5: true
- },
- { text: 'true', type: 'boolean', p5: 'boolean' },
- { text: 'false', type: 'boolean', p5: 'boolean' },
- { text: 'await', type: 'keyword', p5: false },
- { text: 'break', type: 'keyword', p5: false },
- { text: 'case', type: 'keyword', p5: false },
- { text: 'catch', type: 'keyword', p5: false },
- { text: 'class', type: 'keyword', p5: 'class' },
- { text: 'const', type: 'keyword', p5: 'const' },
- { text: 'continue', type: 'keyword', p5: false },
- { text: 'debugger', type: 'keyword', p5: false },
- { text: 'default', type: 'keyword', p5: false },
- { text: 'delete', type: 'keyword', p5: false },
- { text: 'do', type: 'keyword', p5: false },
- { text: 'else', type: 'keyword', p5: 'if-else' },
- { text: 'export', type: 'keyword', p5: false },
- { text: 'extends', type: 'keyword', p5: false },
- { text: 'finally', type: 'keyword', p5: false },
- { text: 'for', type: 'keyword', p5: 'for' },
- { text: 'function', type: 'keyword', p5: 'function' },
- { text: 'if', type: 'keyword', p5: 'if-else' },
- { text: 'import', type: 'keyword', p5: false },
- { text: 'in', type: 'keyword', p5: false },
- { text: 'instanceof', type: 'keyword', p5: false },
- { text: 'new', type: 'keyword', p5: false },
- { text: 'return', type: 'keyword', p5: 'return' },
- { text: 'super', type: 'keyword', p5: false },
- { text: 'switch', type: 'keyword', p5: false },
- { text: 'this', type: 'keyword', p5: false },
- { text: 'throw', type: 'keyword', p5: false },
- { text: 'try', type: 'keyword', p5: false },
- { text: 'typeof', type: 'keyword', p5: false },
- { text: 'var', type: 'keyword', p5: false },
- { text: 'void', type: 'keyword', p5: false },
- { text: 'while', type: 'keyword', p5: 'while' },
- { text: 'with', type: 'keyword', p5: false },
- { text: 'yield', type: 'keyword', p5: false },
- { text: 'let', type: 'keyword', p5: 'let' },
- { text: 'Array', type: 'obj', p5: false },
- { text: 'Boolean', type: 'obj', p5: false },
- { text: 'Date', type: 'obj', p5: false },
- { text: 'Error', type: 'obj', p5: false },
- { text: 'Function', type: 'obj', p5: false },
- { text: 'JSON', type: 'obj', p5: 'JSON' },
- { text: 'Math', type: 'obj', p5: false },
- { text: 'Number', type: 'obj', p5: false },
- { text: 'Object', type: 'obj', p5: false },
- { text: 'RegExp', type: 'obj', p5: false },
- { text: 'String', type: 'obj', p5: false },
- { text: 'Promise', type: 'obj', p5: false },
- { text: 'Set', type: 'obj', p5: false },
- { text: 'Map', type: 'obj', p5: false },
- { text: 'Symbol', type: 'obj', p5: false },
- { text: 'WeakMap', type: 'obj', p5: false },
- { text: 'WeakSet', type: 'obj', p5: false },
- { text: 'ArrayBuffer', type: 'obj', p5: false },
- { text: 'DataView', type: 'obj', p5: false },
- { text: 'Int32Array', type: 'obj', p5: false },
- { text: 'Uint32Array', type: 'obj', p5: false },
- { text: 'Float32Array', type: 'obj', p5: false },
- { text: 'window', type: 'obj', p5: false },
- { text: 'document', type: 'obj', p5: false },
- { text: 'navigator', type: 'obj', p5: false },
- { text: 'console', type: 'obj', p5: 'console' },
- { text: 'localStorage', type: 'obj', p5: false },
- { text: 'sessionStorage', type: 'obj', p5: false },
- { text: 'history', type: 'obj', p5: false },
- { text: 'location', type: 'obj', p5: false }
-];
+exports.p5Hinter = [{"label":"describe","type":"method","kindLabel":"fun","params":[{"p":"text","o":false},{"p":"display","o":true}],"preview":"describe(text, [display])","p5DocPath":"describe"},{"label":"describeElement","type":"method","kindLabel":"fun","params":[{"p":"name","o":false},{"p":"text","o":false},{"p":"display","o":true}],"preview":"describeElement(name, text, [display])","p5DocPath":"describeElement"},{"label":"textOutput","type":"method","kindLabel":"fun","params":[{"p":"display","o":true}],"preview":"textOutput([display])","p5DocPath":"textOutput"},{"label":"gridOutput","type":"method","kindLabel":"fun","params":[{"p":"display","o":true}],"preview":"gridOutput([display])","p5DocPath":"gridOutput"},{"label":"alpha","type":"method","kindLabel":"fun","params":[{"p":"color","o":false}],"preview":"alpha(color)","p5DocPath":"alpha"},{"label":"blue","type":"method","kindLabel":"fun","params":[{"p":"color","o":false}],"preview":"blue(color)","p5DocPath":"blue"},{"label":"brightness","type":"method","kindLabel":"fun","params":[{"p":"color","o":false}],"preview":"brightness(color)","p5DocPath":"brightness"},{"label":"color","type":"method","kindLabel":"fun","preview":"color()","p5DocPath":"color"},{"label":"green","type":"method","kindLabel":"fun","params":[{"p":"color","o":false}],"preview":"green(color)","p5DocPath":"green"},{"label":"hue","type":"method","kindLabel":"fun","params":[{"p":"color","o":false}],"preview":"hue(color)","p5DocPath":"hue"},{"label":"lerpColor","type":"method","kindLabel":"fun","params":[{"p":"c1","o":false},{"p":"c2","o":false},{"p":"amt","o":false}],"preview":"lerpColor(c1, c2, amt)","p5DocPath":"lerpColor"},{"label":"paletteLerp","type":"method","kindLabel":"fun","params":[{"p":"colors_stops","o":false},{"p":"amt","o":false}],"preview":"paletteLerp(colors_stops, amt)","p5DocPath":"paletteLerp"},{"label":"lightness","type":"method","kindLabel":"fun","params":[{"p":"color","o":false}],"preview":"lightness(color)","p5DocPath":"lightness"},{"label":"red","type":"method","kindLabel":"fun","params":[{"p":"color","o":false}],"preview":"red(color)","p5DocPath":"red"},{"label":"saturation","type":"method","kindLabel":"fun","params":[{"p":"color","o":false}],"preview":"saturation(color)","p5DocPath":"saturation"},{"label":"beginClip","type":"method","kindLabel":"fun","params":[{"p":"options","o":true}],"preview":"beginClip([options])","p5DocPath":"beginClip"},{"label":"endClip","type":"method","kindLabel":"fun","preview":"endClip()","p5DocPath":"endClip"},{"label":"clip","type":"method","kindLabel":"fun","params":[{"p":"callback","o":false},{"p":"options","o":true}],"preview":"clip(callback, [options])","p5DocPath":"clip"},{"label":"background","type":"method","kindLabel":"fun","preview":"background()","p5DocPath":"background"},{"label":"clear","type":"method","kindLabel":"fun","params":[{"p":"r","o":true},{"p":"g","o":true},{"p":"b","o":true},{"p":"a","o":true}],"preview":"clear([r], [g], [b], [a])","p5DocPath":"clear"},{"label":"colorMode","type":"method","kindLabel":"fun","preview":"colorMode()","p5DocPath":"colorMode"},{"label":"fill","type":"method","kindLabel":"fun","preview":"fill()","p5DocPath":"fill"},{"label":"noFill","type":"method","kindLabel":"fun","preview":"noFill()","p5DocPath":"noFill"},{"label":"noStroke","type":"method","kindLabel":"fun","preview":"noStroke()","p5DocPath":"noStroke"},{"label":"stroke","type":"method","kindLabel":"fun","preview":"stroke()","p5DocPath":"stroke"},{"label":"erase","type":"method","kindLabel":"fun","params":[{"p":"strengthFill","o":true},{"p":"strengthStroke","o":true}],"preview":"erase([strengthFill], [strengthStroke])","p5DocPath":"erase"},{"label":"noErase","type":"method","kindLabel":"fun","preview":"noErase()","p5DocPath":"noErase"},{"label":"arc","type":"method","kindLabel":"fun","params":[{"p":"x","o":false},{"p":"y","o":false},{"p":"w","o":false},{"p":"h","o":false},{"p":"start","o":false},{"p":"stop","o":false},{"p":"mode","o":true},{"p":"detail","o":true}],"preview":"arc(x, y, w, h, start, stop, [mode], [detail])","p5DocPath":"arc"},{"label":"ellipse","type":"method","kindLabel":"fun","preview":"ellipse()","p5DocPath":"ellipse"},{"label":"circle","type":"method","kindLabel":"fun","params":[{"p":"x","o":false},{"p":"y","o":false},{"p":"d","o":false}],"preview":"circle(x, y, d)","p5DocPath":"circle"},{"label":"line","type":"method","kindLabel":"fun","preview":"line()","p5DocPath":"line"},{"label":"point","type":"method","kindLabel":"fun","preview":"point()","p5DocPath":"point"},{"label":"quad","type":"method","kindLabel":"fun","preview":"quad()","p5DocPath":"quad"},{"label":"rect","type":"method","kindLabel":"fun","preview":"rect()","p5DocPath":"rect"},{"label":"square","type":"method","kindLabel":"fun","params":[{"p":"x","o":false},{"p":"y","o":false},{"p":"s","o":false},{"p":"tl","o":true},{"p":"tr","o":true},{"p":"br","o":true},{"p":"bl","o":true}],"preview":"square(x, y, s, [tl], [tr], [br], [bl])","p5DocPath":"square"},{"label":"triangle","type":"method","kindLabel":"fun","params":[{"p":"x1","o":false},{"p":"y1","o":false},{"p":"x2","o":false},{"p":"y2","o":false},{"p":"x3","o":false},{"p":"y3","o":false}],"preview":"triangle(x1, y1, x2, y2, x3, y3)","p5DocPath":"triangle"},{"label":"ellipseMode","type":"method","kindLabel":"fun","params":[{"p":"mode","o":false}],"preview":"ellipseMode(mode)","p5DocPath":"ellipseMode"},{"label":"noSmooth","type":"method","kindLabel":"fun","preview":"noSmooth()","p5DocPath":"noSmooth"},{"label":"rectMode","type":"method","kindLabel":"fun","params":[{"p":"mode","o":false}],"preview":"rectMode(mode)","p5DocPath":"rectMode"},{"label":"smooth","type":"method","kindLabel":"fun","preview":"smooth()","p5DocPath":"smooth"},{"label":"strokeCap","type":"method","kindLabel":"fun","params":[{"p":"cap","o":false}],"preview":"strokeCap(cap)","p5DocPath":"strokeCap"},{"label":"strokeJoin","type":"method","kindLabel":"fun","params":[{"p":"join","o":false}],"preview":"strokeJoin(join)","p5DocPath":"strokeJoin"},{"label":"strokeWeight","type":"method","kindLabel":"fun","params":[{"p":"weight","o":false}],"preview":"strokeWeight(weight)","p5DocPath":"strokeWeight"},{"label":"bezier","type":"method","kindLabel":"fun","preview":"bezier()","p5DocPath":"bezier"},{"label":"bezierDetail","type":"method","kindLabel":"fun","params":[{"p":"detail","o":false}],"preview":"bezierDetail(detail)","p5DocPath":"bezierDetail"},{"label":"bezierPoint","type":"method","kindLabel":"fun","params":[{"p":"a","o":false},{"p":"b","o":false},{"p":"c","o":false},{"p":"d","o":false},{"p":"t","o":false}],"preview":"bezierPoint(a, b, c, d, t)","p5DocPath":"bezierPoint"},{"label":"bezierTangent","type":"method","kindLabel":"fun","params":[{"p":"a","o":false},{"p":"b","o":false},{"p":"c","o":false},{"p":"d","o":false},{"p":"t","o":false}],"preview":"bezierTangent(a, b, c, d, t)","p5DocPath":"bezierTangent"},{"label":"curve","type":"method","kindLabel":"fun","preview":"curve()","p5DocPath":"curve"},{"label":"curveDetail","type":"method","kindLabel":"fun","params":[{"p":"resolution","o":false}],"preview":"curveDetail(resolution)","p5DocPath":"curveDetail"},{"label":"curveTightness","type":"method","kindLabel":"fun","params":[{"p":"amount","o":false}],"preview":"curveTightness(amount)","p5DocPath":"curveTightness"},{"label":"curvePoint","type":"method","kindLabel":"fun","params":[{"p":"a","o":false},{"p":"b","o":false},{"p":"c","o":false},{"p":"d","o":false},{"p":"t","o":false}],"preview":"curvePoint(a, b, c, d, t)","p5DocPath":"curvePoint"},{"label":"curveTangent","type":"method","kindLabel":"fun","params":[{"p":"a","o":false},{"p":"b","o":false},{"p":"c","o":false},{"p":"d","o":false},{"p":"t","o":false}],"preview":"curveTangent(a, b, c, d, t)","p5DocPath":"curveTangent"},{"label":"beginContour","type":"method","kindLabel":"fun","preview":"beginContour()","p5DocPath":"beginContour"},{"label":"beginShape","type":"method","kindLabel":"fun","params":[{"p":"kind","o":true}],"preview":"beginShape([kind])","p5DocPath":"beginShape"},{"label":"bezierVertex","type":"method","kindLabel":"fun","preview":"bezierVertex()","p5DocPath":"bezierVertex"},{"label":"curveVertex","type":"method","kindLabel":"fun","preview":"curveVertex()","p5DocPath":"curveVertex"},{"label":"endContour","type":"method","kindLabel":"fun","preview":"endContour()","p5DocPath":"endContour"},{"label":"endShape","type":"method","kindLabel":"fun","params":[{"p":"mode","o":true},{"p":"count","o":true}],"preview":"endShape([mode], [count])","p5DocPath":"endShape"},{"label":"quadraticVertex","type":"method","kindLabel":"fun","preview":"quadraticVertex()","p5DocPath":"quadraticVertex"},{"label":"vertex","type":"method","kindLabel":"fun","preview":"vertex()","p5DocPath":"vertex"},{"label":"normal","type":"method","kindLabel":"fun","preview":"normal()","p5DocPath":"normal"},{"label":"VERSION","type":"constant","kindLabel":"const","params":[],"preview":"VERSION","p5DocPath":"VERSION"},{"label":"P2D","type":"constant","kindLabel":"const","params":[],"preview":"P2D","p5DocPath":"P2D"},{"label":"WEBGL","type":"constant","kindLabel":"const","params":[],"preview":"WEBGL","p5DocPath":"WEBGL"},{"label":"WEBGL2","type":"constant","kindLabel":"const","params":[],"preview":"WEBGL2","p5DocPath":"WEBGL2"},{"label":"ARROW","type":"constant","kindLabel":"const","params":[],"preview":"ARROW","p5DocPath":"ARROW"},{"label":"CROSS","type":"constant","kindLabel":"const","params":[],"preview":"CROSS","p5DocPath":"CROSS"},{"label":"HAND","type":"constant","kindLabel":"const","params":[],"preview":"HAND","p5DocPath":"HAND"},{"label":"MOVE","type":"constant","kindLabel":"const","params":[],"preview":"MOVE","p5DocPath":"MOVE"},{"label":"TEXT","type":"constant","kindLabel":"const","params":[],"preview":"TEXT","p5DocPath":"TEXT"},{"label":"WAIT","type":"constant","kindLabel":"const","params":[],"preview":"WAIT","p5DocPath":"WAIT"},{"label":"HALF_PI","type":"constant","kindLabel":"const","params":[],"preview":"HALF_PI","p5DocPath":"HALF_PI"},{"label":"PI","type":"constant","kindLabel":"const","params":[],"preview":"PI","p5DocPath":"PI"},{"label":"QUARTER_PI","type":"constant","kindLabel":"const","params":[],"preview":"QUARTER_PI","p5DocPath":"QUARTER_PI"},{"label":"TAU","type":"constant","kindLabel":"const","params":[],"preview":"TAU","p5DocPath":"TAU"},{"label":"TWO_PI","type":"constant","kindLabel":"const","params":[],"preview":"TWO_PI","p5DocPath":"TWO_PI"},{"label":"DEGREES","type":"constant","kindLabel":"const","params":[],"preview":"DEGREES","p5DocPath":"DEGREES"},{"label":"RADIANS","type":"constant","kindLabel":"const","params":[],"preview":"RADIANS","p5DocPath":"RADIANS"},{"label":"CORNER","type":"constant","kindLabel":"const","params":[],"preview":"CORNER","p5DocPath":"CORNER"},{"label":"CORNERS","type":"constant","kindLabel":"const","params":[],"preview":"CORNERS","p5DocPath":"CORNERS"},{"label":"RADIUS","type":"constant","kindLabel":"const","params":[],"preview":"RADIUS","p5DocPath":"RADIUS"},{"label":"RIGHT","type":"constant","kindLabel":"const","params":[],"preview":"RIGHT","p5DocPath":"RIGHT"},{"label":"LEFT","type":"constant","kindLabel":"const","params":[],"preview":"LEFT","p5DocPath":"LEFT"},{"label":"CENTER","type":"constant","kindLabel":"const","params":[],"preview":"CENTER","p5DocPath":"CENTER"},{"label":"TOP","type":"constant","kindLabel":"const","params":[],"preview":"TOP","p5DocPath":"TOP"},{"label":"BOTTOM","type":"constant","kindLabel":"const","params":[],"preview":"BOTTOM","p5DocPath":"BOTTOM"},{"label":"BASELINE","type":"constant","kindLabel":"const","params":[],"preview":"BASELINE","p5DocPath":"BASELINE"},{"label":"POINTS","type":"constant","kindLabel":"const","params":[],"preview":"POINTS","p5DocPath":"POINTS"},{"label":"LINES","type":"constant","kindLabel":"const","params":[],"preview":"LINES","p5DocPath":"LINES"},{"label":"LINE_STRIP","type":"constant","kindLabel":"const","params":[],"preview":"LINE_STRIP","p5DocPath":"LINE_STRIP"},{"label":"LINE_LOOP","type":"constant","kindLabel":"const","params":[],"preview":"LINE_LOOP","p5DocPath":"LINE_LOOP"},{"label":"TRIANGLES","type":"constant","kindLabel":"const","params":[],"preview":"TRIANGLES","p5DocPath":"TRIANGLES"},{"label":"TRIANGLE_FAN","type":"constant","kindLabel":"const","params":[],"preview":"TRIANGLE_FAN","p5DocPath":"TRIANGLE_FAN"},{"label":"TRIANGLE_STRIP","type":"constant","kindLabel":"const","params":[],"preview":"TRIANGLE_STRIP","p5DocPath":"TRIANGLE_STRIP"},{"label":"QUADS","type":"constant","kindLabel":"const","params":[],"preview":"QUADS","p5DocPath":"QUADS"},{"label":"QUAD_STRIP","type":"constant","kindLabel":"const","params":[],"preview":"QUAD_STRIP","p5DocPath":"QUAD_STRIP"},{"label":"TESS","type":"constant","kindLabel":"const","params":[],"preview":"TESS","p5DocPath":"TESS"},{"label":"CLOSE","type":"constant","kindLabel":"const","params":[],"preview":"CLOSE","p5DocPath":"CLOSE"},{"label":"OPEN","type":"constant","kindLabel":"const","params":[],"preview":"OPEN","p5DocPath":"OPEN"},{"label":"CHORD","type":"constant","kindLabel":"const","params":[],"preview":"CHORD","p5DocPath":"CHORD"},{"label":"PIE","type":"constant","kindLabel":"const","params":[],"preview":"PIE","p5DocPath":"PIE"},{"label":"PROJECT","type":"constant","kindLabel":"const","params":[],"preview":"PROJECT","p5DocPath":"PROJECT"},{"label":"SQUARE","type":"constant","kindLabel":"const","params":[],"preview":"SQUARE","p5DocPath":"SQUARE"},{"label":"ROUND","type":"constant","kindLabel":"const","params":[],"preview":"ROUND","p5DocPath":"ROUND"},{"label":"BEVEL","type":"constant","kindLabel":"const","params":[],"preview":"BEVEL","p5DocPath":"BEVEL"},{"label":"MITER","type":"constant","kindLabel":"const","params":[],"preview":"MITER","p5DocPath":"MITER"},{"label":"RGB","type":"constant","kindLabel":"const","params":[],"preview":"RGB","p5DocPath":"RGB"},{"label":"HSB","type":"constant","kindLabel":"const","params":[],"preview":"HSB","p5DocPath":"HSB"},{"label":"HSL","type":"constant","kindLabel":"const","params":[],"preview":"HSL","p5DocPath":"HSL"},{"label":"AUTO","type":"constant","kindLabel":"const","params":[],"preview":"AUTO","p5DocPath":"AUTO"},{"label":"ALT","type":"constant","kindLabel":"const","params":[],"preview":"ALT","p5DocPath":"ALT"},{"label":"BACKSPACE","type":"constant","kindLabel":"const","params":[],"preview":"BACKSPACE","p5DocPath":"BACKSPACE"},{"label":"CONTROL","type":"constant","kindLabel":"const","params":[],"preview":"CONTROL","p5DocPath":"CONTROL"},{"label":"DELETE","type":"constant","kindLabel":"const","params":[],"preview":"DELETE","p5DocPath":"DELETE"},{"label":"DOWN_ARROW","type":"constant","kindLabel":"const","params":[],"preview":"DOWN_ARROW","p5DocPath":"DOWN_ARROW"},{"label":"ENTER","type":"constant","kindLabel":"const","params":[],"preview":"ENTER","p5DocPath":"ENTER"},{"label":"ESCAPE","type":"constant","kindLabel":"const","params":[],"preview":"ESCAPE","p5DocPath":"ESCAPE"},{"label":"LEFT_ARROW","type":"constant","kindLabel":"const","params":[],"preview":"LEFT_ARROW","p5DocPath":"LEFT_ARROW"},{"label":"OPTION","type":"constant","kindLabel":"const","params":[],"preview":"OPTION","p5DocPath":"OPTION"},{"label":"RETURN","type":"constant","kindLabel":"const","params":[],"preview":"RETURN","p5DocPath":"RETURN"},{"label":"RIGHT_ARROW","type":"constant","kindLabel":"const","params":[],"preview":"RIGHT_ARROW","p5DocPath":"RIGHT_ARROW"},{"label":"SHIFT","type":"constant","kindLabel":"const","params":[],"preview":"SHIFT","p5DocPath":"SHIFT"},{"label":"TAB","type":"constant","kindLabel":"const","params":[],"preview":"TAB","p5DocPath":"TAB"},{"label":"UP_ARROW","type":"constant","kindLabel":"const","params":[],"preview":"UP_ARROW","p5DocPath":"UP_ARROW"},{"label":"BLEND","type":"constant","kindLabel":"const","params":[],"preview":"BLEND","p5DocPath":"BLEND"},{"label":"REMOVE","type":"constant","kindLabel":"const","params":[],"preview":"REMOVE","p5DocPath":"REMOVE"},{"label":"ADD","type":"constant","kindLabel":"const","params":[],"preview":"ADD","p5DocPath":"ADD"},{"label":"DARKEST","type":"constant","kindLabel":"const","params":[],"preview":"DARKEST","p5DocPath":"DARKEST"},{"label":"LIGHTEST","type":"constant","kindLabel":"const","params":[],"preview":"LIGHTEST","p5DocPath":"LIGHTEST"},{"label":"DIFFERENCE","type":"constant","kindLabel":"const","params":[],"preview":"DIFFERENCE","p5DocPath":"DIFFERENCE"},{"label":"SUBTRACT","type":"constant","kindLabel":"const","params":[],"preview":"SUBTRACT","p5DocPath":"SUBTRACT"},{"label":"EXCLUSION","type":"constant","kindLabel":"const","params":[],"preview":"EXCLUSION","p5DocPath":"EXCLUSION"},{"label":"MULTIPLY","type":"constant","kindLabel":"const","params":[],"preview":"MULTIPLY","p5DocPath":"MULTIPLY"},{"label":"SCREEN","type":"constant","kindLabel":"const","params":[],"preview":"SCREEN","p5DocPath":"SCREEN"},{"label":"REPLACE","type":"constant","kindLabel":"const","params":[],"preview":"REPLACE","p5DocPath":"REPLACE"},{"label":"OVERLAY","type":"constant","kindLabel":"const","params":[],"preview":"OVERLAY","p5DocPath":"OVERLAY"},{"label":"HARD_LIGHT","type":"constant","kindLabel":"const","params":[],"preview":"HARD_LIGHT","p5DocPath":"HARD_LIGHT"},{"label":"SOFT_LIGHT","type":"constant","kindLabel":"const","params":[],"preview":"SOFT_LIGHT","p5DocPath":"SOFT_LIGHT"},{"label":"DODGE","type":"constant","kindLabel":"const","params":[],"preview":"DODGE","p5DocPath":"DODGE"},{"label":"BURN","type":"constant","kindLabel":"const","params":[],"preview":"BURN","p5DocPath":"BURN"},{"label":"THRESHOLD","type":"constant","kindLabel":"const","params":[],"preview":"THRESHOLD","p5DocPath":"THRESHOLD"},{"label":"GRAY","type":"constant","kindLabel":"const","params":[],"preview":"GRAY","p5DocPath":"GRAY"},{"label":"OPAQUE","type":"constant","kindLabel":"const","params":[],"preview":"OPAQUE","p5DocPath":"OPAQUE"},{"label":"INVERT","type":"constant","kindLabel":"const","params":[],"preview":"INVERT","p5DocPath":"INVERT"},{"label":"POSTERIZE","type":"constant","kindLabel":"const","params":[],"preview":"POSTERIZE","p5DocPath":"POSTERIZE"},{"label":"DILATE","type":"constant","kindLabel":"const","params":[],"preview":"DILATE","p5DocPath":"DILATE"},{"label":"ERODE","type":"constant","kindLabel":"const","params":[],"preview":"ERODE","p5DocPath":"ERODE"},{"label":"BLUR","type":"constant","kindLabel":"const","params":[],"preview":"BLUR","p5DocPath":"BLUR"},{"label":"NORMAL","type":"constant","kindLabel":"const","params":[],"preview":"NORMAL","p5DocPath":"NORMAL"},{"label":"ITALIC","type":"constant","kindLabel":"const","params":[],"preview":"ITALIC","p5DocPath":"ITALIC"},{"label":"BOLD","type":"constant","kindLabel":"const","params":[],"preview":"BOLD","p5DocPath":"BOLD"},{"label":"BOLDITALIC","type":"constant","kindLabel":"const","params":[],"preview":"BOLDITALIC","p5DocPath":"BOLDITALIC"},{"label":"CHAR","type":"constant","kindLabel":"const","params":[],"preview":"CHAR","p5DocPath":"CHAR"},{"label":"WORD","type":"constant","kindLabel":"const","params":[],"preview":"WORD","p5DocPath":"WORD"},{"label":"LINEAR","type":"constant","kindLabel":"const","params":[],"preview":"LINEAR","p5DocPath":"LINEAR"},{"label":"QUADRATIC","type":"constant","kindLabel":"const","params":[],"preview":"QUADRATIC","p5DocPath":"QUADRATIC"},{"label":"BEZIER","type":"constant","kindLabel":"const","params":[],"preview":"BEZIER","p5DocPath":"BEZIER"},{"label":"CURVE","type":"constant","kindLabel":"const","params":[],"preview":"CURVE","p5DocPath":"CURVE"},{"label":"STROKE","type":"constant","kindLabel":"const","params":[],"preview":"STROKE","p5DocPath":"STROKE"},{"label":"FILL","type":"constant","kindLabel":"const","params":[],"preview":"FILL","p5DocPath":"FILL"},{"label":"TEXTURE","type":"constant","kindLabel":"const","params":[],"preview":"TEXTURE","p5DocPath":"TEXTURE"},{"label":"IMMEDIATE","type":"constant","kindLabel":"const","params":[],"preview":"IMMEDIATE","p5DocPath":"IMMEDIATE"},{"label":"IMAGE","type":"constant","kindLabel":"const","params":[],"preview":"IMAGE","p5DocPath":"IMAGE"},{"label":"NEAREST","type":"constant","kindLabel":"const","params":[],"preview":"NEAREST","p5DocPath":"NEAREST"},{"label":"REPEAT","type":"constant","kindLabel":"const","params":[],"preview":"REPEAT","p5DocPath":"REPEAT"},{"label":"CLAMP","type":"constant","kindLabel":"const","params":[],"preview":"CLAMP","p5DocPath":"CLAMP"},{"label":"MIRROR","type":"constant","kindLabel":"const","params":[],"preview":"MIRROR","p5DocPath":"MIRROR"},{"label":"FLAT","type":"constant","kindLabel":"const","params":[],"preview":"FLAT","p5DocPath":"FLAT"},{"label":"SMOOTH","type":"constant","kindLabel":"const","params":[],"preview":"SMOOTH","p5DocPath":"SMOOTH"},{"label":"LANDSCAPE","type":"constant","kindLabel":"const","params":[],"preview":"LANDSCAPE","p5DocPath":"LANDSCAPE"},{"label":"PORTRAIT","type":"constant","kindLabel":"const","params":[],"preview":"PORTRAIT","p5DocPath":"PORTRAIT"},{"label":"GRID","type":"constant","kindLabel":"const","params":[],"preview":"GRID","p5DocPath":"GRID"},{"label":"AXES","type":"constant","kindLabel":"const","params":[],"preview":"AXES","p5DocPath":"AXES"},{"label":"LABEL","type":"constant","kindLabel":"const","params":[],"preview":"LABEL","p5DocPath":"LABEL"},{"label":"FALLBACK","type":"constant","kindLabel":"const","params":[],"preview":"FALLBACK","p5DocPath":"FALLBACK"},{"label":"CONTAIN","type":"constant","kindLabel":"const","params":[],"preview":"CONTAIN","p5DocPath":"CONTAIN"},{"label":"COVER","type":"constant","kindLabel":"const","params":[],"preview":"COVER","p5DocPath":"COVER"},{"label":"UNSIGNED_BYTE","type":"constant","kindLabel":"const","params":[],"preview":"UNSIGNED_BYTE","p5DocPath":"UNSIGNED_BYTE"},{"label":"UNSIGNED_INT","type":"constant","kindLabel":"const","params":[],"preview":"UNSIGNED_INT","p5DocPath":"UNSIGNED_INT"},{"label":"FLOAT","type":"constant","kindLabel":"const","params":[],"preview":"FLOAT","p5DocPath":"FLOAT"},{"label":"HALF_FLOAT","type":"constant","kindLabel":"const","params":[],"preview":"HALF_FLOAT","p5DocPath":"HALF_FLOAT"},{"label":"RGBA","type":"constant","kindLabel":"const","params":[],"preview":"RGBA","p5DocPath":"RGBA"},{"label":"print","type":"method","kindLabel":"fun","params":[{"p":"contents","o":false}],"preview":"print(contents)","p5DocPath":"print"},{"label":"frameCount","type":"variable","kindLabel":"var","params":[],"preview":"frameCount","p5DocPath":"frameCount"},{"label":"deltaTime","type":"variable","kindLabel":"var","params":[],"preview":"deltaTime","p5DocPath":"deltaTime"},{"label":"focused","type":"variable","kindLabel":"var","params":[],"preview":"focused","p5DocPath":"focused"},{"label":"cursor","type":"method","kindLabel":"fun","params":[{"p":"type","o":false},{"p":"x","o":true},{"p":"y","o":true}],"preview":"cursor(type, [x], [y])","p5DocPath":"cursor"},{"label":"frameRate","type":"method","kindLabel":"fun","preview":"frameRate()","p5DocPath":"frameRate"},{"label":"getTargetFrameRate","type":"method","kindLabel":"fun","preview":"getTargetFrameRate()","p5DocPath":"getTargetFrameRate"},{"label":"noCursor","type":"method","kindLabel":"fun","preview":"noCursor()","p5DocPath":"noCursor"},{"label":"webglVersion","type":"variable","kindLabel":"var","params":[],"preview":"webglVersion","p5DocPath":"webglVersion"},{"label":"displayWidth","type":"variable","kindLabel":"var","params":[],"preview":"displayWidth","p5DocPath":"displayWidth"},{"label":"displayHeight","type":"variable","kindLabel":"var","params":[],"preview":"displayHeight","p5DocPath":"displayHeight"},{"label":"windowWidth","type":"variable","kindLabel":"var","params":[],"preview":"windowWidth","p5DocPath":"windowWidth"},{"label":"windowHeight","type":"variable","kindLabel":"var","params":[],"preview":"windowHeight","p5DocPath":"windowHeight"},{"label":"windowResized","type":"method","kindLabel":"fun","params":[{"p":"event","o":true}],"preview":"windowResized([event])","p5DocPath":"windowResized"},{"label":"width","type":"variable","kindLabel":"var","params":[],"preview":"width","p5DocPath":"width"},{"label":"height","type":"variable","kindLabel":"var","params":[],"preview":"height","p5DocPath":"height"},{"label":"fullscreen","type":"method","kindLabel":"fun","params":[{"p":"val","o":true}],"preview":"fullscreen([val])","p5DocPath":"fullscreen"},{"label":"pixelDensity","type":"method","kindLabel":"fun","preview":"pixelDensity()","p5DocPath":"pixelDensity"},{"label":"displayDensity","type":"method","kindLabel":"fun","preview":"displayDensity()","p5DocPath":"displayDensity"},{"label":"getURL","type":"method","kindLabel":"fun","preview":"getURL()","p5DocPath":"getURL"},{"label":"getURLPath","type":"method","kindLabel":"fun","preview":"getURLPath()","p5DocPath":"getURLPath"},{"label":"getURLParams","type":"method","kindLabel":"fun","preview":"getURLParams()","p5DocPath":"getURLParams"},{"label":"preload","type":"method","kindLabel":"fun","preview":"preload()","p5DocPath":"preload"},{"label":"setup","type":"method","kindLabel":"fun","preview":"setup()","p5DocPath":"setup"},{"label":"draw","type":"method","kindLabel":"fun","preview":"draw()","p5DocPath":"draw"},{"label":"remove","type":"method","kindLabel":"fun","preview":"remove()","p5DocPath":"remove"},{"label":"disableFriendlyErrors","type":"variable","kindLabel":"var","params":[],"preview":"disableFriendlyErrors","p5DocPath":"disableFriendlyErrors"},{"label":"createCanvas","type":"method","kindLabel":"fun","preview":"createCanvas()","p5DocPath":"createCanvas"},{"label":"resizeCanvas","type":"method","kindLabel":"fun","params":[{"p":"width","o":false},{"p":"height","o":false},{"p":"noRedraw","o":true}],"preview":"resizeCanvas(width, height, [noRedraw])","p5DocPath":"resizeCanvas"},{"label":"noCanvas","type":"method","kindLabel":"fun","preview":"noCanvas()","p5DocPath":"noCanvas"},{"label":"createGraphics","type":"method","kindLabel":"fun","preview":"createGraphics()","p5DocPath":"createGraphics"},{"label":"createFramebuffer","type":"method","kindLabel":"fun","params":[{"p":"options","o":true}],"preview":"createFramebuffer([options])","p5DocPath":"createFramebuffer"},{"label":"clearDepth","type":"method","kindLabel":"fun","params":[{"p":"depth","o":true}],"preview":"clearDepth([depth])","p5DocPath":"clearDepth"},{"label":"blendMode","type":"method","kindLabel":"fun","params":[{"p":"mode","o":false}],"preview":"blendMode(mode)","p5DocPath":"blendMode"},{"label":"drawingContext","type":"variable","kindLabel":"var","params":[],"preview":"drawingContext","p5DocPath":"drawingContext"},{"label":"noLoop","type":"method","kindLabel":"fun","preview":"noLoop()","p5DocPath":"noLoop"},{"label":"loop","type":"method","kindLabel":"fun","preview":"loop()","p5DocPath":"loop"},{"label":"isLooping","type":"method","kindLabel":"fun","preview":"isLooping()","p5DocPath":"isLooping"},{"label":"push","type":"method","kindLabel":"fun","preview":"push()","p5DocPath":"push"},{"label":"pop","type":"method","kindLabel":"fun","preview":"pop()","p5DocPath":"pop"},{"label":"redraw","type":"method","kindLabel":"fun","params":[{"p":"n","o":true}],"preview":"redraw([n])","p5DocPath":"redraw"},{"label":"p5","type":"method","kindLabel":"fun","params":[{"p":"sketch","o":false},{"p":"node","o":false}],"preview":"p5(sketch, node)","p5DocPath":"p5"},{"label":"applyMatrix","type":"method","kindLabel":"fun","preview":"applyMatrix()","p5DocPath":"applyMatrix"},{"label":"resetMatrix","type":"method","kindLabel":"fun","preview":"resetMatrix()","p5DocPath":"resetMatrix"},{"label":"rotate","type":"method","kindLabel":"fun","params":[{"p":"angle","o":false},{"p":"axis","o":true}],"preview":"rotate(angle, [axis])","p5DocPath":"rotate"},{"label":"rotateX","type":"method","kindLabel":"fun","params":[{"p":"angle","o":false}],"preview":"rotateX(angle)","p5DocPath":"rotateX"},{"label":"rotateY","type":"method","kindLabel":"fun","params":[{"p":"angle","o":false}],"preview":"rotateY(angle)","p5DocPath":"rotateY"},{"label":"rotateZ","type":"method","kindLabel":"fun","params":[{"p":"angle","o":false}],"preview":"rotateZ(angle)","p5DocPath":"rotateZ"},{"label":"scale","type":"method","kindLabel":"fun","preview":"scale()","p5DocPath":"scale"},{"label":"shearX","type":"method","kindLabel":"fun","params":[{"p":"angle","o":false}],"preview":"shearX(angle)","p5DocPath":"shearX"},{"label":"shearY","type":"method","kindLabel":"fun","params":[{"p":"angle","o":false}],"preview":"shearY(angle)","p5DocPath":"shearY"},{"label":"translate","type":"method","kindLabel":"fun","preview":"translate()","p5DocPath":"translate"},{"label":"storeItem","type":"method","kindLabel":"fun","params":[{"p":"key","o":false},{"p":"value","o":false}],"preview":"storeItem(key, value)","p5DocPath":"storeItem"},{"label":"getItem","type":"method","kindLabel":"fun","params":[{"p":"key","o":false}],"preview":"getItem(key)","p5DocPath":"getItem"},{"label":"clearStorage","type":"method","kindLabel":"fun","preview":"clearStorage()","p5DocPath":"clearStorage"},{"label":"removeItem","type":"method","kindLabel":"fun","params":[{"p":"key","o":false}],"preview":"removeItem(key)","p5DocPath":"removeItem"},{"label":"createStringDict","type":"method","kindLabel":"fun","preview":"createStringDict()","p5DocPath":"createStringDict"},{"label":"createNumberDict","type":"method","kindLabel":"fun","preview":"createNumberDict()","p5DocPath":"createNumberDict"},{"label":"select","type":"method","kindLabel":"fun","params":[{"p":"selectors","o":false},{"p":"container","o":true}],"preview":"select(selectors, [container])","p5DocPath":"select"},{"label":"selectAll","type":"method","kindLabel":"fun","params":[{"p":"selectors","o":false},{"p":"container","o":true}],"preview":"selectAll(selectors, [container])","p5DocPath":"selectAll"},{"label":"removeElements","type":"method","kindLabel":"fun","preview":"removeElements()","p5DocPath":"removeElements"},{"label":"changed","type":"method","kindLabel":"fun","params":[{"p":"fxn","o":false}],"preview":"changed(fxn)","p5DocPath":"changed"},{"label":"input","type":"method","kindLabel":"fun","params":[{"p":"fxn","o":false}],"preview":"input(fxn)","p5DocPath":"input"},{"label":"createDiv","type":"method","kindLabel":"fun","params":[{"p":"html","o":true}],"preview":"createDiv([html])","p5DocPath":"createDiv"},{"label":"createP","type":"method","kindLabel":"fun","params":[{"p":"html","o":true}],"preview":"createP([html])","p5DocPath":"createP"},{"label":"createSpan","type":"method","kindLabel":"fun","params":[{"p":"html","o":true}],"preview":"createSpan([html])","p5DocPath":"createSpan"},{"label":"createImg","type":"method","kindLabel":"fun","preview":"createImg()","p5DocPath":"createImg"},{"label":"createA","type":"method","kindLabel":"fun","params":[{"p":"href","o":false},{"p":"html","o":false},{"p":"target","o":true}],"preview":"createA(href, html, [target])","p5DocPath":"createA"},{"label":"createSlider","type":"method","kindLabel":"fun","params":[{"p":"min","o":false},{"p":"max","o":false},{"p":"value","o":true},{"p":"step","o":true}],"preview":"createSlider(min, max, [value], [step])","p5DocPath":"createSlider"},{"label":"createButton","type":"method","kindLabel":"fun","params":[{"p":"label","o":false},{"p":"value","o":true}],"preview":"createButton(label, [value])","p5DocPath":"createButton"},{"label":"createCheckbox","type":"method","kindLabel":"fun","params":[{"p":"label","o":true},{"p":"value","o":true}],"preview":"createCheckbox([label], [value])","p5DocPath":"createCheckbox"},{"label":"createSelect","type":"method","kindLabel":"fun","preview":"createSelect()","p5DocPath":"createSelect"},{"label":"createRadio","type":"method","kindLabel":"fun","preview":"createRadio()","p5DocPath":"createRadio"},{"label":"createColorPicker","type":"method","kindLabel":"fun","params":[{"p":"value","o":true}],"preview":"createColorPicker([value])","p5DocPath":"createColorPicker"},{"label":"createInput","type":"method","kindLabel":"fun","preview":"createInput()","p5DocPath":"createInput"},{"label":"createFileInput","type":"method","kindLabel":"fun","params":[{"p":"callback","o":false},{"p":"multiple","o":true}],"preview":"createFileInput(callback, [multiple])","p5DocPath":"createFileInput"},{"label":"createVideo","type":"method","kindLabel":"fun","params":[{"p":"src","o":false},{"p":"callback","o":true}],"preview":"createVideo(src, [callback])","p5DocPath":"createVideo"},{"label":"createAudio","type":"method","kindLabel":"fun","params":[{"p":"src","o":true},{"p":"callback","o":true}],"preview":"createAudio([src], [callback])","p5DocPath":"createAudio"},{"label":"createCapture","type":"method","kindLabel":"fun","params":[{"p":"type","o":true},{"p":"flipped","o":true},{"p":"callback","o":true}],"preview":"createCapture([type], [flipped], [callback])","p5DocPath":"createCapture"},{"label":"createElement","type":"method","kindLabel":"fun","params":[{"p":"tag","o":false},{"p":"content","o":true}],"preview":"createElement(tag, [content])","p5DocPath":"createElement"},{"label":"deviceOrientation","type":"variable","kindLabel":"var","params":[],"preview":"deviceOrientation","p5DocPath":"deviceOrientation"},{"label":"accelerationX","type":"variable","kindLabel":"var","params":[],"preview":"accelerationX","p5DocPath":"accelerationX"},{"label":"accelerationY","type":"variable","kindLabel":"var","params":[],"preview":"accelerationY","p5DocPath":"accelerationY"},{"label":"accelerationZ","type":"variable","kindLabel":"var","params":[],"preview":"accelerationZ","p5DocPath":"accelerationZ"},{"label":"pAccelerationX","type":"variable","kindLabel":"var","params":[],"preview":"pAccelerationX","p5DocPath":"pAccelerationX"},{"label":"pAccelerationY","type":"variable","kindLabel":"var","params":[],"preview":"pAccelerationY","p5DocPath":"pAccelerationY"},{"label":"pAccelerationZ","type":"variable","kindLabel":"var","params":[],"preview":"pAccelerationZ","p5DocPath":"pAccelerationZ"},{"label":"rotationX","type":"variable","kindLabel":"var","params":[],"preview":"rotationX","p5DocPath":"rotationX"},{"label":"rotationY","type":"variable","kindLabel":"var","params":[],"preview":"rotationY","p5DocPath":"rotationY"},{"label":"rotationZ","type":"variable","kindLabel":"var","params":[],"preview":"rotationZ","p5DocPath":"rotationZ"},{"label":"pRotationX","type":"variable","kindLabel":"var","params":[],"preview":"pRotationX","p5DocPath":"pRotationX"},{"label":"pRotationY","type":"variable","kindLabel":"var","params":[],"preview":"pRotationY","p5DocPath":"pRotationY"},{"label":"pRotationZ","type":"variable","kindLabel":"var","params":[],"preview":"pRotationZ","p5DocPath":"pRotationZ"},{"label":"turnAxis","type":"variable","kindLabel":"var","params":[],"preview":"turnAxis","p5DocPath":"turnAxis"},{"label":"setMoveThreshold","type":"method","kindLabel":"fun","params":[{"p":"value","o":false}],"preview":"setMoveThreshold(value)","p5DocPath":"setMoveThreshold"},{"label":"setShakeThreshold","type":"method","kindLabel":"fun","params":[{"p":"value","o":false}],"preview":"setShakeThreshold(value)","p5DocPath":"setShakeThreshold"},{"label":"deviceMoved","type":"method","kindLabel":"fun","preview":"deviceMoved()","p5DocPath":"deviceMoved"},{"label":"deviceTurned","type":"method","kindLabel":"fun","preview":"deviceTurned()","p5DocPath":"deviceTurned"},{"label":"deviceShaken","type":"method","kindLabel":"fun","preview":"deviceShaken()","p5DocPath":"deviceShaken"},{"label":"keyIsPressed","type":"variable","kindLabel":"var","params":[],"preview":"keyIsPressed","p5DocPath":"keyIsPressed"},{"label":"key","type":"variable","kindLabel":"var","params":[],"preview":"key","p5DocPath":"key"},{"label":"keyCode","type":"variable","kindLabel":"var","params":[],"preview":"keyCode","p5DocPath":"keyCode"},{"label":"keyPressed","type":"method","kindLabel":"fun","params":[{"p":"event","o":true}],"preview":"keyPressed([event])","p5DocPath":"keyPressed"},{"label":"keyReleased","type":"method","kindLabel":"fun","params":[{"p":"event","o":true}],"preview":"keyReleased([event])","p5DocPath":"keyReleased"},{"label":"keyTyped","type":"method","kindLabel":"fun","params":[{"p":"event","o":true}],"preview":"keyTyped([event])","p5DocPath":"keyTyped"},{"label":"keyIsDown","type":"method","kindLabel":"fun","params":[{"p":"code","o":false}],"preview":"keyIsDown(code)","p5DocPath":"keyIsDown"},{"label":"movedX","type":"variable","kindLabel":"var","params":[],"preview":"movedX","p5DocPath":"movedX"},{"label":"movedY","type":"variable","kindLabel":"var","params":[],"preview":"movedY","p5DocPath":"movedY"},{"label":"mouseX","type":"variable","kindLabel":"var","params":[],"preview":"mouseX","p5DocPath":"mouseX"},{"label":"mouseY","type":"variable","kindLabel":"var","params":[],"preview":"mouseY","p5DocPath":"mouseY"},{"label":"pmouseX","type":"variable","kindLabel":"var","params":[],"preview":"pmouseX","p5DocPath":"pmouseX"},{"label":"pmouseY","type":"variable","kindLabel":"var","params":[],"preview":"pmouseY","p5DocPath":"pmouseY"},{"label":"winMouseX","type":"variable","kindLabel":"var","params":[],"preview":"winMouseX","p5DocPath":"winMouseX"},{"label":"winMouseY","type":"variable","kindLabel":"var","params":[],"preview":"winMouseY","p5DocPath":"winMouseY"},{"label":"pwinMouseX","type":"variable","kindLabel":"var","params":[],"preview":"pwinMouseX","p5DocPath":"pwinMouseX"},{"label":"pwinMouseY","type":"variable","kindLabel":"var","params":[],"preview":"pwinMouseY","p5DocPath":"pwinMouseY"},{"label":"mouseButton","type":"variable","kindLabel":"var","params":[],"preview":"mouseButton","p5DocPath":"mouseButton"},{"label":"mouseIsPressed","type":"variable","kindLabel":"var","params":[],"preview":"mouseIsPressed","p5DocPath":"mouseIsPressed"},{"label":"mouseMoved","type":"method","kindLabel":"fun","params":[{"p":"event","o":true}],"preview":"mouseMoved([event])","p5DocPath":"mouseMoved"},{"label":"mouseDragged","type":"method","kindLabel":"fun","params":[{"p":"event","o":true}],"preview":"mouseDragged([event])","p5DocPath":"mouseDragged"},{"label":"mousePressed","type":"method","kindLabel":"fun","params":[{"p":"event","o":true}],"preview":"mousePressed([event])","p5DocPath":"mousePressed"},{"label":"mouseReleased","type":"method","kindLabel":"fun","params":[{"p":"event","o":true}],"preview":"mouseReleased([event])","p5DocPath":"mouseReleased"},{"label":"mouseClicked","type":"method","kindLabel":"fun","params":[{"p":"event","o":true}],"preview":"mouseClicked([event])","p5DocPath":"mouseClicked"},{"label":"doubleClicked","type":"method","kindLabel":"fun","params":[{"p":"event","o":true}],"preview":"doubleClicked([event])","p5DocPath":"doubleClicked"},{"label":"mouseWheel","type":"method","kindLabel":"fun","params":[{"p":"event","o":true}],"preview":"mouseWheel([event])","p5DocPath":"mouseWheel"},{"label":"requestPointerLock","type":"method","kindLabel":"fun","preview":"requestPointerLock()","p5DocPath":"requestPointerLock"},{"label":"exitPointerLock","type":"method","kindLabel":"fun","preview":"exitPointerLock()","p5DocPath":"exitPointerLock"},{"label":"touches","type":"variable","kindLabel":"var","params":[],"preview":"touches","p5DocPath":"touches"},{"label":"touchStarted","type":"method","kindLabel":"fun","params":[{"p":"event","o":true}],"preview":"touchStarted([event])","p5DocPath":"touchStarted"},{"label":"touchMoved","type":"method","kindLabel":"fun","params":[{"p":"event","o":true}],"preview":"touchMoved([event])","p5DocPath":"touchMoved"},{"label":"touchEnded","type":"method","kindLabel":"fun","params":[{"p":"event","o":true}],"preview":"touchEnded([event])","p5DocPath":"touchEnded"},{"label":"createImage","type":"method","kindLabel":"fun","params":[{"p":"width","o":false},{"p":"height","o":false}],"preview":"createImage(width, height)","p5DocPath":"createImage"},{"label":"saveCanvas","type":"method","kindLabel":"fun","preview":"saveCanvas()","p5DocPath":"saveCanvas"},{"label":"saveFrames","type":"method","kindLabel":"fun","params":[{"p":"filename","o":false},{"p":"extension","o":false},{"p":"duration","o":false},{"p":"framerate","o":false},{"p":"callback","o":true}],"preview":"saveFrames(filename, extension, duration, framerate, [callback])","p5DocPath":"saveFrames"},{"label":"loadImage","type":"method","kindLabel":"fun","params":[{"p":"path","o":false},{"p":"successCallback","o":true},{"p":"failureCallback","o":true}],"preview":"loadImage(path, [successCallback], [failureCallback])","p5DocPath":"loadImage"},{"label":"saveGif","type":"method","kindLabel":"fun","params":[{"p":"filename","o":false},{"p":"duration","o":false},{"p":"options","o":true}],"preview":"saveGif(filename, duration, [options])","p5DocPath":"saveGif"},{"label":"image","type":"method","kindLabel":"fun","preview":"image()","p5DocPath":"image"},{"label":"tint","type":"method","kindLabel":"fun","preview":"tint()","p5DocPath":"tint"},{"label":"noTint","type":"method","kindLabel":"fun","preview":"noTint()","p5DocPath":"noTint"},{"label":"imageMode","type":"method","kindLabel":"fun","params":[{"p":"mode","o":false}],"preview":"imageMode(mode)","p5DocPath":"imageMode"},{"label":"pixels","type":"variable","kindLabel":"var","params":[],"preview":"pixels","p5DocPath":"pixels"},{"label":"blend","type":"method","kindLabel":"fun","preview":"blend()","p5DocPath":"blend"},{"label":"copy","type":"method","kindLabel":"fun","preview":"copy()","p5DocPath":"copy"},{"label":"filter","type":"method","kindLabel":"fun","preview":"filter()","p5DocPath":"filter"},{"label":"get","type":"method","kindLabel":"fun","preview":"get()","p5DocPath":"get"},{"label":"loadPixels","type":"method","kindLabel":"fun","preview":"loadPixels()","p5DocPath":"loadPixels"},{"label":"set","type":"method","kindLabel":"fun","params":[{"p":"x","o":false},{"p":"y","o":false},{"p":"c","o":false}],"preview":"set(x, y, c)","p5DocPath":"set"},{"label":"updatePixels","type":"method","kindLabel":"fun","params":[{"p":"x","o":true},{"p":"y","o":true},{"p":"w","o":true},{"p":"h","o":true}],"preview":"updatePixels([x], [y], [w], [h])","p5DocPath":"updatePixels"},{"label":"loadJSON","type":"method","kindLabel":"fun","params":[{"p":"path","o":false},{"p":"successCallback","o":true},{"p":"errorCallback","o":true}],"preview":"loadJSON(path, [successCallback], [errorCallback])","p5DocPath":"loadJSON"},{"label":"loadStrings","type":"method","kindLabel":"fun","params":[{"p":"path","o":false},{"p":"successCallback","o":true},{"p":"errorCallback","o":true}],"preview":"loadStrings(path, [successCallback], [errorCallback])","p5DocPath":"loadStrings"},{"label":"loadTable","type":"method","kindLabel":"fun","params":[{"p":"filename","o":false},{"p":"extension","o":true},{"p":"header","o":true},{"p":"callback","o":true},{"p":"errorCallback","o":true}],"preview":"loadTable(filename, [extension], [header], [callback], [errorCallback])","p5DocPath":"loadTable"},{"label":"loadXML","type":"method","kindLabel":"fun","params":[{"p":"path","o":false},{"p":"successCallback","o":true},{"p":"errorCallback","o":true}],"preview":"loadXML(path, [successCallback], [errorCallback])","p5DocPath":"loadXML"},{"label":"loadBytes","type":"method","kindLabel":"fun","params":[{"p":"file","o":false},{"p":"callback","o":true},{"p":"errorCallback","o":true}],"preview":"loadBytes(file, [callback], [errorCallback])","p5DocPath":"loadBytes"},{"label":"httpGet","type":"method","kindLabel":"fun","preview":"httpGet()","p5DocPath":"httpGet"},{"label":"httpPost","type":"method","kindLabel":"fun","preview":"httpPost()","p5DocPath":"httpPost"},{"label":"httpDo","type":"method","kindLabel":"fun","preview":"httpDo()","p5DocPath":"httpDo"},{"label":"createWriter","type":"method","kindLabel":"fun","params":[{"p":"name","o":false},{"p":"extension","o":true}],"preview":"createWriter(name, [extension])","p5DocPath":"createWriter"},{"label":"save","type":"method","kindLabel":"fun","params":[{"p":"objectOrFilename","o":true},{"p":"filename","o":true},{"p":"options","o":true}],"preview":"save([objectOrFilename], [filename], [options])","p5DocPath":"save"},{"label":"saveJSON","type":"method","kindLabel":"fun","params":[{"p":"json","o":false},{"p":"filename","o":false},{"p":"optimize","o":true}],"preview":"saveJSON(json, filename, [optimize])","p5DocPath":"saveJSON"},{"label":"saveStrings","type":"method","kindLabel":"fun","params":[{"p":"list","o":false},{"p":"filename","o":false},{"p":"extension","o":true},{"p":"isCRLF","o":true}],"preview":"saveStrings(list, filename, [extension], [isCRLF])","p5DocPath":"saveStrings"},{"label":"saveTable","type":"method","kindLabel":"fun","params":[{"p":"Table","o":false},{"p":"filename","o":false},{"p":"options","o":true}],"preview":"saveTable(Table, filename, [options])","p5DocPath":"saveTable"},{"label":"abs","type":"method","kindLabel":"fun","params":[{"p":"n","o":false}],"preview":"abs(n)","p5DocPath":"abs"},{"label":"ceil","type":"method","kindLabel":"fun","params":[{"p":"n","o":false}],"preview":"ceil(n)","p5DocPath":"ceil"},{"label":"constrain","type":"method","kindLabel":"fun","params":[{"p":"n","o":false},{"p":"low","o":false},{"p":"high","o":false}],"preview":"constrain(n, low, high)","p5DocPath":"constrain"},{"label":"dist","type":"method","kindLabel":"fun","preview":"dist()","p5DocPath":"dist"},{"label":"exp","type":"method","kindLabel":"fun","params":[{"p":"n","o":false}],"preview":"exp(n)","p5DocPath":"exp"},{"label":"floor","type":"method","kindLabel":"fun","params":[{"p":"n","o":false}],"preview":"floor(n)","p5DocPath":"floor"},{"label":"lerp","type":"method","kindLabel":"fun","params":[{"p":"start","o":false},{"p":"stop","o":false},{"p":"amt","o":false}],"preview":"lerp(start, stop, amt)","p5DocPath":"lerp"},{"label":"log","type":"method","kindLabel":"fun","params":[{"p":"n","o":false}],"preview":"log(n)","p5DocPath":"log"},{"label":"mag","type":"method","kindLabel":"fun","params":[{"p":"x","o":false},{"p":"y","o":false}],"preview":"mag(x, y)","p5DocPath":"mag"},{"label":"map","type":"method","kindLabel":"fun","params":[{"p":"value","o":false},{"p":"start1","o":false},{"p":"stop1","o":false},{"p":"start2","o":false},{"p":"stop2","o":false},{"p":"withinBounds","o":true}],"preview":"map(value, start1, stop1, start2, stop2, [withinBounds])","p5DocPath":"map"},{"label":"max","type":"method","kindLabel":"fun","preview":"max()","p5DocPath":"max"},{"label":"min","type":"method","kindLabel":"fun","preview":"min()","p5DocPath":"min"},{"label":"norm","type":"method","kindLabel":"fun","params":[{"p":"value","o":false},{"p":"start","o":false},{"p":"stop","o":false}],"preview":"norm(value, start, stop)","p5DocPath":"norm"},{"label":"pow","type":"method","kindLabel":"fun","params":[{"p":"n","o":false},{"p":"e","o":false}],"preview":"pow(n, e)","p5DocPath":"pow"},{"label":"round","type":"method","kindLabel":"fun","params":[{"p":"n","o":false},{"p":"decimals","o":true}],"preview":"round(n, [decimals])","p5DocPath":"round"},{"label":"sq","type":"method","kindLabel":"fun","params":[{"p":"n","o":false}],"preview":"sq(n)","p5DocPath":"sq"},{"label":"sqrt","type":"method","kindLabel":"fun","params":[{"p":"n","o":false}],"preview":"sqrt(n)","p5DocPath":"sqrt"},{"label":"fract","type":"method","kindLabel":"fun","params":[{"p":"n","o":false}],"preview":"fract(n)","p5DocPath":"fract"},{"label":"createVector","type":"method","kindLabel":"fun","params":[{"p":"x","o":true},{"p":"y","o":true},{"p":"z","o":true}],"preview":"createVector([x], [y], [z])","p5DocPath":"createVector"},{"label":"noise","type":"method","kindLabel":"fun","params":[{"p":"x","o":false},{"p":"y","o":true},{"p":"z","o":true}],"preview":"noise(x, [y], [z])","p5DocPath":"noise"},{"label":"noiseDetail","type":"method","kindLabel":"fun","params":[{"p":"lod","o":false},{"p":"falloff","o":false}],"preview":"noiseDetail(lod, falloff)","p5DocPath":"noiseDetail"},{"label":"noiseSeed","type":"method","kindLabel":"fun","params":[{"p":"seed","o":false}],"preview":"noiseSeed(seed)","p5DocPath":"noiseSeed"},{"label":"randomSeed","type":"method","kindLabel":"fun","params":[{"p":"seed","o":false}],"preview":"randomSeed(seed)","p5DocPath":"randomSeed"},{"label":"random","type":"method","kindLabel":"fun","preview":"random()","p5DocPath":"random"},{"label":"randomGaussian","type":"method","kindLabel":"fun","params":[{"p":"mean","o":true},{"p":"sd","o":true}],"preview":"randomGaussian([mean], [sd])","p5DocPath":"randomGaussian"},{"label":"acos","type":"method","kindLabel":"fun","params":[{"p":"value","o":false}],"preview":"acos(value)","p5DocPath":"acos"},{"label":"asin","type":"method","kindLabel":"fun","params":[{"p":"value","o":false}],"preview":"asin(value)","p5DocPath":"asin"},{"label":"atan","type":"method","kindLabel":"fun","params":[{"p":"value","o":false}],"preview":"atan(value)","p5DocPath":"atan"},{"label":"atan2","type":"method","kindLabel":"fun","params":[{"p":"y","o":false},{"p":"x","o":false}],"preview":"atan2(y, x)","p5DocPath":"atan2"},{"label":"cos","type":"method","kindLabel":"fun","params":[{"p":"angle","o":false}],"preview":"cos(angle)","p5DocPath":"cos"},{"label":"sin","type":"method","kindLabel":"fun","params":[{"p":"angle","o":false}],"preview":"sin(angle)","p5DocPath":"sin"},{"label":"tan","type":"method","kindLabel":"fun","params":[{"p":"angle","o":false}],"preview":"tan(angle)","p5DocPath":"tan"},{"label":"degrees","type":"method","kindLabel":"fun","params":[{"p":"radians","o":false}],"preview":"degrees(radians)","p5DocPath":"degrees"},{"label":"radians","type":"method","kindLabel":"fun","params":[{"p":"degrees","o":false}],"preview":"radians(degrees)","p5DocPath":"radians"},{"label":"angleMode","type":"method","kindLabel":"fun","preview":"angleMode()","p5DocPath":"angleMode"},{"label":"textAlign","type":"method","kindLabel":"fun","preview":"textAlign()","p5DocPath":"textAlign"},{"label":"textLeading","type":"method","kindLabel":"fun","preview":"textLeading()","p5DocPath":"textLeading"},{"label":"textSize","type":"method","kindLabel":"fun","preview":"textSize()","p5DocPath":"textSize"},{"label":"textStyle","type":"method","kindLabel":"fun","preview":"textStyle()","p5DocPath":"textStyle"},{"label":"textWidth","type":"method","kindLabel":"fun","params":[{"p":"str","o":false}],"preview":"textWidth(str)","p5DocPath":"textWidth"},{"label":"textAscent","type":"method","kindLabel":"fun","preview":"textAscent()","p5DocPath":"textAscent"},{"label":"textDescent","type":"method","kindLabel":"fun","preview":"textDescent()","p5DocPath":"textDescent"},{"label":"textWrap","type":"method","kindLabel":"fun","params":[{"p":"style","o":false}],"preview":"textWrap(style)","p5DocPath":"textWrap"},{"label":"loadFont","type":"method","kindLabel":"fun","params":[{"p":"path","o":false},{"p":"successCallback","o":true},{"p":"failureCallback","o":true}],"preview":"loadFont(path, [successCallback], [failureCallback])","p5DocPath":"loadFont"},{"label":"text","type":"method","kindLabel":"fun","params":[{"p":"str","o":false},{"p":"x","o":false},{"p":"y","o":false},{"p":"maxWidth","o":true},{"p":"maxHeight","o":true}],"preview":"text(str, x, y, [maxWidth], [maxHeight])","p5DocPath":"text"},{"label":"textFont","type":"method","kindLabel":"fun","preview":"textFont()","p5DocPath":"textFont"},{"label":"append","type":"method","kindLabel":"fun","params":[{"p":"array","o":false},{"p":"value","o":false}],"preview":"append(array, value)","p5DocPath":"append"},{"label":"arrayCopy","type":"method","kindLabel":"fun","preview":"arrayCopy()","p5DocPath":"arrayCopy"},{"label":"concat","type":"method","kindLabel":"fun","params":[{"p":"a","o":false},{"p":"b","o":false}],"preview":"concat(a, b)","p5DocPath":"concat"},{"label":"reverse","type":"method","kindLabel":"fun","params":[{"p":"list","o":false}],"preview":"reverse(list)","p5DocPath":"reverse"},{"label":"shorten","type":"method","kindLabel":"fun","params":[{"p":"list","o":false}],"preview":"shorten(list)","p5DocPath":"shorten"},{"label":"shuffle","type":"method","kindLabel":"fun","params":[{"p":"array","o":false},{"p":"bool","o":true}],"preview":"shuffle(array, [bool])","p5DocPath":"shuffle"},{"label":"sort","type":"method","kindLabel":"fun","params":[{"p":"list","o":false},{"p":"count","o":true}],"preview":"sort(list, [count])","p5DocPath":"sort"},{"label":"splice","type":"method","kindLabel":"fun","params":[{"p":"list","o":false},{"p":"value","o":false},{"p":"position","o":false}],"preview":"splice(list, value, position)","p5DocPath":"splice"},{"label":"subset","type":"method","kindLabel":"fun","params":[{"p":"list","o":false},{"p":"start","o":false},{"p":"count","o":true}],"preview":"subset(list, start, [count])","p5DocPath":"subset"},{"label":"float","type":"method","kindLabel":"fun","preview":"float()","p5DocPath":"float"},{"label":"int","type":"method","kindLabel":"fun","preview":"int()","p5DocPath":"int"},{"label":"str","type":"method","kindLabel":"fun","params":[{"p":"n","o":false}],"preview":"str(n)","p5DocPath":"str"},{"label":"boolean","type":"method","kindLabel":"fun","preview":"boolean()","p5DocPath":"boolean"},{"label":"byte","type":"method","kindLabel":"fun","preview":"byte()","p5DocPath":"byte"},{"label":"char","type":"method","kindLabel":"fun","preview":"char()","p5DocPath":"char"},{"label":"unchar","type":"method","kindLabel":"fun","preview":"unchar()","p5DocPath":"unchar"},{"label":"hex","type":"method","kindLabel":"fun","preview":"hex()","p5DocPath":"hex"},{"label":"unhex","type":"method","kindLabel":"fun","preview":"unhex()","p5DocPath":"unhex"},{"label":"join","type":"method","kindLabel":"fun","params":[{"p":"list","o":false},{"p":"separator","o":false}],"preview":"join(list, separator)","p5DocPath":"join"},{"label":"match","type":"method","kindLabel":"fun","params":[{"p":"str","o":false},{"p":"regexp","o":false}],"preview":"match(str, regexp)","p5DocPath":"match"},{"label":"matchAll","type":"method","kindLabel":"fun","params":[{"p":"str","o":false},{"p":"regexp","o":false}],"preview":"matchAll(str, regexp)","p5DocPath":"matchAll"},{"label":"nf","type":"method","kindLabel":"fun","preview":"nf()","p5DocPath":"nf"},{"label":"nfc","type":"method","kindLabel":"fun","preview":"nfc()","p5DocPath":"nfc"},{"label":"nfp","type":"method","kindLabel":"fun","preview":"nfp()","p5DocPath":"nfp"},{"label":"nfs","type":"method","kindLabel":"fun","preview":"nfs()","p5DocPath":"nfs"},{"label":"split","type":"method","kindLabel":"fun","params":[{"p":"value","o":false},{"p":"delim","o":false}],"preview":"split(value, delim)","p5DocPath":"split"},{"label":"splitTokens","type":"method","kindLabel":"fun","params":[{"p":"value","o":false},{"p":"delim","o":true}],"preview":"splitTokens(value, [delim])","p5DocPath":"splitTokens"},{"label":"trim","type":"method","kindLabel":"fun","preview":"trim()","p5DocPath":"trim"},{"label":"day","type":"method","kindLabel":"fun","preview":"day()","p5DocPath":"day"},{"label":"hour","type":"method","kindLabel":"fun","preview":"hour()","p5DocPath":"hour"},{"label":"minute","type":"method","kindLabel":"fun","preview":"minute()","p5DocPath":"minute"},{"label":"millis","type":"method","kindLabel":"fun","preview":"millis()","p5DocPath":"millis"},{"label":"month","type":"method","kindLabel":"fun","preview":"month()","p5DocPath":"month"},{"label":"second","type":"method","kindLabel":"fun","preview":"second()","p5DocPath":"second"},{"label":"year","type":"method","kindLabel":"fun","preview":"year()","p5DocPath":"year"},{"label":"beginGeometry","type":"method","kindLabel":"fun","preview":"beginGeometry()","p5DocPath":"beginGeometry"},{"label":"endGeometry","type":"method","kindLabel":"fun","preview":"endGeometry()","p5DocPath":"endGeometry"},{"label":"buildGeometry","type":"method","kindLabel":"fun","params":[{"p":"callback","o":false}],"preview":"buildGeometry(callback)","p5DocPath":"buildGeometry"},{"label":"freeGeometry","type":"method","kindLabel":"fun","params":[{"p":"geometry","o":false}],"preview":"freeGeometry(geometry)","p5DocPath":"freeGeometry"},{"label":"plane","type":"method","kindLabel":"fun","params":[{"p":"width","o":true},{"p":"height","o":true},{"p":"detailX","o":true},{"p":"detailY","o":true}],"preview":"plane([width], [height], [detailX], [detailY])","p5DocPath":"plane"},{"label":"box","type":"method","kindLabel":"fun","params":[{"p":"width","o":true},{"p":"height","o":true},{"p":"depth","o":true},{"p":"detailX","o":true},{"p":"detailY","o":true}],"preview":"box([width], [height], [depth], [detailX], [detailY])","p5DocPath":"box"},{"label":"sphere","type":"method","kindLabel":"fun","params":[{"p":"radius","o":true},{"p":"detailX","o":true},{"p":"detailY","o":true}],"preview":"sphere([radius], [detailX], [detailY])","p5DocPath":"sphere"},{"label":"cylinder","type":"method","kindLabel":"fun","params":[{"p":"radius","o":true},{"p":"height","o":true},{"p":"detailX","o":true},{"p":"detailY","o":true},{"p":"bottomCap","o":true},{"p":"topCap","o":true}],"preview":"cylinder([radius], [height], [detailX], [detailY], [bottomCap], [topCap])","p5DocPath":"cylinder"},{"label":"cone","type":"method","kindLabel":"fun","params":[{"p":"radius","o":true},{"p":"height","o":true},{"p":"detailX","o":true},{"p":"detailY","o":true},{"p":"cap","o":true}],"preview":"cone([radius], [height], [detailX], [detailY], [cap])","p5DocPath":"cone"},{"label":"ellipsoid","type":"method","kindLabel":"fun","params":[{"p":"radiusX","o":true},{"p":"radiusY","o":true},{"p":"radiusZ","o":true},{"p":"detailX","o":true},{"p":"detailY","o":true}],"preview":"ellipsoid([radiusX], [radiusY], [radiusZ], [detailX], [detailY])","p5DocPath":"ellipsoid"},{"label":"torus","type":"method","kindLabel":"fun","params":[{"p":"radius","o":true},{"p":"tubeRadius","o":true},{"p":"detailX","o":true},{"p":"detailY","o":true}],"preview":"torus([radius], [tubeRadius], [detailX], [detailY])","p5DocPath":"torus"},{"label":"orbitControl","type":"method","kindLabel":"fun","params":[{"p":"sensitivityX","o":true},{"p":"sensitivityY","o":true},{"p":"sensitivityZ","o":true},{"p":"options","o":true}],"preview":"orbitControl([sensitivityX], [sensitivityY], [sensitivityZ], [options])","p5DocPath":"orbitControl"},{"label":"debugMode","type":"method","kindLabel":"fun","preview":"debugMode()","p5DocPath":"debugMode"},{"label":"noDebugMode","type":"method","kindLabel":"fun","preview":"noDebugMode()","p5DocPath":"noDebugMode"},{"label":"ambientLight","type":"method","kindLabel":"fun","preview":"ambientLight()","p5DocPath":"ambientLight"},{"label":"specularColor","type":"method","kindLabel":"fun","preview":"specularColor()","p5DocPath":"specularColor"},{"label":"directionalLight","type":"method","kindLabel":"fun","preview":"directionalLight()","p5DocPath":"directionalLight"},{"label":"pointLight","type":"method","kindLabel":"fun","preview":"pointLight()","p5DocPath":"pointLight"},{"label":"imageLight","type":"method","kindLabel":"fun","params":[{"p":"img","o":false}],"preview":"imageLight(img)","p5DocPath":"imageLight"},{"label":"panorama","type":"method","kindLabel":"fun","params":[{"p":"img","o":false}],"preview":"panorama(img)","p5DocPath":"panorama"},{"label":"lights","type":"method","kindLabel":"fun","preview":"lights()","p5DocPath":"lights"},{"label":"lightFalloff","type":"method","kindLabel":"fun","params":[{"p":"constant","o":false},{"p":"linear","o":false},{"p":"quadratic","o":false}],"preview":"lightFalloff(constant, linear, quadratic)","p5DocPath":"lightFalloff"},{"label":"spotLight","type":"method","kindLabel":"fun","preview":"spotLight()","p5DocPath":"spotLight"},{"label":"noLights","type":"method","kindLabel":"fun","preview":"noLights()","p5DocPath":"noLights"},{"label":"loadModel","type":"method","kindLabel":"fun","preview":"loadModel()","p5DocPath":"loadModel"},{"label":"model","type":"method","kindLabel":"fun","params":[{"p":"model","o":false}],"preview":"model(model)","p5DocPath":"model"},{"label":"createModel","type":"method","kindLabel":"fun","preview":"createModel()","p5DocPath":"createModel"},{"label":"loadShader","type":"method","kindLabel":"fun","params":[{"p":"vertFilename","o":false},{"p":"fragFilename","o":false},{"p":"successCallback","o":true},{"p":"failureCallback","o":true}],"preview":"loadShader(vertFilename, fragFilename, [successCallback], [failureCallback])","p5DocPath":"loadShader"},{"label":"createShader","type":"method","kindLabel":"fun","params":[{"p":"vertSrc","o":false},{"p":"fragSrc","o":false},{"p":"options","o":true}],"preview":"createShader(vertSrc, fragSrc, [options])","p5DocPath":"createShader"},{"label":"createFilterShader","type":"method","kindLabel":"fun","params":[{"p":"fragSrc","o":false}],"preview":"createFilterShader(fragSrc)","p5DocPath":"createFilterShader"},{"label":"shader","type":"method","kindLabel":"fun","params":[{"p":"s","o":false}],"preview":"shader(s)","p5DocPath":"shader"},{"label":"baseMaterialShader","type":"method","kindLabel":"fun","preview":"baseMaterialShader()","p5DocPath":"baseMaterialShader"},{"label":"baseNormalShader","type":"method","kindLabel":"fun","preview":"baseNormalShader()","p5DocPath":"baseNormalShader"},{"label":"baseColorShader","type":"method","kindLabel":"fun","preview":"baseColorShader()","p5DocPath":"baseColorShader"},{"label":"baseStrokeShader","type":"method","kindLabel":"fun","preview":"baseStrokeShader()","p5DocPath":"baseStrokeShader"},{"label":"resetShader","type":"method","kindLabel":"fun","preview":"resetShader()","p5DocPath":"resetShader"},{"label":"texture","type":"method","kindLabel":"fun","params":[{"p":"tex","o":false}],"preview":"texture(tex)","p5DocPath":"texture"},{"label":"textureMode","type":"method","kindLabel":"fun","params":[{"p":"mode","o":false}],"preview":"textureMode(mode)","p5DocPath":"textureMode"},{"label":"textureWrap","type":"method","kindLabel":"fun","params":[{"p":"wrapX","o":false},{"p":"wrapY","o":true}],"preview":"textureWrap(wrapX, [wrapY])","p5DocPath":"textureWrap"},{"label":"normalMaterial","type":"method","kindLabel":"fun","preview":"normalMaterial()","p5DocPath":"normalMaterial"},{"label":"ambientMaterial","type":"method","kindLabel":"fun","preview":"ambientMaterial()","p5DocPath":"ambientMaterial"},{"label":"emissiveMaterial","type":"method","kindLabel":"fun","preview":"emissiveMaterial()","p5DocPath":"emissiveMaterial"},{"label":"specularMaterial","type":"method","kindLabel":"fun","preview":"specularMaterial()","p5DocPath":"specularMaterial"},{"label":"shininess","type":"method","kindLabel":"fun","params":[{"p":"shine","o":false}],"preview":"shininess(shine)","p5DocPath":"shininess"},{"label":"metalness","type":"method","kindLabel":"fun","params":[{"p":"metallic","o":false}],"preview":"metalness(metallic)","p5DocPath":"metalness"},{"label":"camera","type":"method","kindLabel":"fun","params":[{"p":"x","o":true},{"p":"y","o":true},{"p":"z","o":true},{"p":"centerX","o":true},{"p":"centerY","o":true},{"p":"centerZ","o":true},{"p":"upX","o":true},{"p":"upY","o":true},{"p":"upZ","o":true}],"preview":"camera([x], [y], [z], [centerX], [centerY], [centerZ], [upX], [upY], [upZ])","p5DocPath":"camera"},{"label":"perspective","type":"method","kindLabel":"fun","params":[{"p":"fovy","o":true},{"p":"aspect","o":true},{"p":"near","o":true},{"p":"far","o":true}],"preview":"perspective([fovy], [aspect], [near], [far])","p5DocPath":"perspective"},{"label":"linePerspective","type":"method","kindLabel":"fun","preview":"linePerspective()","p5DocPath":"linePerspective"},{"label":"ortho","type":"method","kindLabel":"fun","params":[{"p":"left","o":true},{"p":"right","o":true},{"p":"bottom","o":true},{"p":"top","o":true},{"p":"near","o":true},{"p":"far","o":true}],"preview":"ortho([left], [right], [bottom], [top], [near], [far])","p5DocPath":"ortho"},{"label":"frustum","type":"method","kindLabel":"fun","params":[{"p":"left","o":true},{"p":"right","o":true},{"p":"bottom","o":true},{"p":"top","o":true},{"p":"near","o":true},{"p":"far","o":true}],"preview":"frustum([left], [right], [bottom], [top], [near], [far])","p5DocPath":"frustum"},{"label":"createCamera","type":"method","kindLabel":"fun","preview":"createCamera()","p5DocPath":"createCamera"},{"label":"setCamera","type":"method","kindLabel":"fun","params":[{"p":"cam","o":false}],"preview":"setCamera(cam)","p5DocPath":"setCamera"},{"label":"setAttributes","type":"method","kindLabel":"fun","preview":"setAttributes()","p5DocPath":"setAttributes"},{"label":"getAudioContext","type":"method","kindLabel":"fun","preview":"getAudioContext()","p5DocPath":"getAudioContext"},{"label":"userStartAudio","type":"method","kindLabel":"fun","params":[{"p":"elements","o":true},{"p":"callback","o":true}],"preview":"userStartAudio([elements], [callback])","p5DocPath":"userStartAudio"},{"label":"getOutputVolume","type":"method","kindLabel":"fun","preview":"getOutputVolume()","p5DocPath":"getOutputVolume"},{"label":"outputVolume","type":"method","kindLabel":"fun","params":[{"p":"volume","o":false},{"p":"rampTime","o":true},{"p":"timeFromNow","o":true}],"preview":"outputVolume(volume, [rampTime], [timeFromNow])","p5DocPath":"outputVolume"},{"label":"soundOut","type":"variable","kindLabel":"var","params":[],"preview":"soundOut","p5DocPath":"soundOut"},{"label":"sampleRate","type":"method","kindLabel":"fun","preview":"sampleRate()","p5DocPath":"sampleRate"},{"label":"freqToMidi","type":"method","kindLabel":"fun","params":[{"p":"frequency","o":false}],"preview":"freqToMidi(frequency)","p5DocPath":"freqToMidi"},{"label":"midiToFreq","type":"method","kindLabel":"fun","params":[{"p":"midiNote","o":false}],"preview":"midiToFreq(midiNote)","p5DocPath":"midiToFreq"},{"label":"soundFormats","type":"method","kindLabel":"fun","params":[{"p":"formats","o":true}],"preview":"soundFormats([formats])","p5DocPath":"soundFormats"},{"label":"saveSound","type":"method","kindLabel":"fun","params":[{"p":"soundFile","o":false},{"p":"fileName","o":false}],"preview":"saveSound(soundFile, fileName)","p5DocPath":"saveSound"},{"label":"loadSound","type":"method","kindLabel":"fun","params":[{"p":"path","o":false},{"p":"successCallback","o":true},{"p":"errorCallback","o":true},{"p":"whileLoading","o":true}],"preview":"loadSound(path, [successCallback], [errorCallback], [whileLoading])","p5DocPath":"loadSound"},{"label":"createConvolver","type":"method","kindLabel":"fun","params":[{"p":"path","o":false},{"p":"callback","o":true},{"p":"errorCallback","o":true}],"preview":"createConvolver(path, [callback], [errorCallback])","p5DocPath":"createConvolver"},{"label":"setBPM","type":"method","kindLabel":"fun","params":[{"p":"BPM","o":false},{"p":"rampTime","o":false}],"preview":"setBPM(BPM, rampTime)","p5DocPath":"setBPM"},{"label":"true","type":"boolean","kindLabel":"bool","params":[],"preview":"true","p5DocPath":"boolean"},{"label":"false","type":"boolean","kindLabel":"bool","params":[],"preview":"false","p5DocPath":"boolean"},{"label":"await","type":"keyword","kindLabel":"keyword","params":[],"preview":"await"},{"label":"class","type":"keyword","kindLabel":"keyword","params":[],"preview":"class","p5DocPath":"class"},{"label":"const","type":"keyword","kindLabel":"keyword","params":[],"preview":"const","p5DocPath":"const"},{"label":"else","type":"keyword","kindLabel":"keyword","params":[],"preview":"else","p5DocPath":"if-else"},{"label":"export","type":"keyword","kindLabel":"keyword","params":[],"preview":"export"},{"label":"for","type":"keyword","kindLabel":"keyword","params":[],"preview":"for","p5DocPath":"for"},{"label":"function","type":"keyword","kindLabel":"keyword","params":[],"preview":"function","p5DocPath":"function"},{"label":"if","type":"keyword","kindLabel":"keyword","params":[],"preview":"if","p5DocPath":"if-else"},{"label":"return","type":"keyword","kindLabel":"keyword","params":[],"preview":"return","p5DocPath":"return"},{"label":"while","type":"keyword","kindLabel":"keyword","params":[],"preview":"while","p5DocPath":"while"},{"label":"with","type":"keyword","kindLabel":"keyword","params":[],"preview":"with"},{"label":"let","type":"keyword","kindLabel":"keyword","params":[],"preview":"let","p5DocPath":"let"},{"label":"Array","type":"obj","kindLabel":"obj","params":[],"preview":"Array"},{"label":"Boolean","type":"obj","kindLabel":"obj","params":[],"preview":"Boolean"},{"label":"Date","type":"obj","kindLabel":"obj","params":[],"preview":"Date"},{"label":"Error","type":"obj","kindLabel":"obj","params":[],"preview":"Error"},{"label":"Function","type":"obj","kindLabel":"obj","params":[],"preview":"Function"},{"label":"JSON","type":"obj","kindLabel":"obj","params":[],"preview":"JSON","p5DocPath":"JSON"},{"label":"Math","type":"obj","kindLabel":"obj","params":[],"preview":"Math"},{"label":"Number","type":"obj","kindLabel":"obj","params":[],"preview":"Number"},{"label":"Object","type":"obj","kindLabel":"obj","params":[],"preview":"Object"},{"label":"RegExp","type":"obj","kindLabel":"obj","params":[],"preview":"RegExp"},{"label":"String","type":"obj","kindLabel":"obj","params":[],"preview":"String"},{"label":"Promise","type":"obj","kindLabel":"obj","params":[],"preview":"Promise"},{"label":"Set","type":"obj","kindLabel":"obj","params":[],"preview":"Set"},{"label":"Map","type":"obj","kindLabel":"obj","params":[],"preview":"Map"},{"label":"Symbol","type":"obj","kindLabel":"obj","params":[],"preview":"Symbol"},{"label":"WeakMap","type":"obj","kindLabel":"obj","params":[],"preview":"WeakMap"},{"label":"WeakSet","type":"obj","kindLabel":"obj","params":[],"preview":"WeakSet"},{"label":"ArrayBuffer","type":"obj","kindLabel":"obj","params":[],"preview":"ArrayBuffer"},{"label":"DataView","type":"obj","kindLabel":"obj","params":[],"preview":"DataView"},{"label":"Int32Array","type":"obj","kindLabel":"obj","params":[],"preview":"Int32Array"},{"label":"Uint32Array","type":"obj","kindLabel":"obj","params":[],"preview":"Uint32Array"},{"label":"Float32Array","type":"obj","kindLabel":"obj","params":[],"preview":"Float32Array"},{"label":"window","type":"obj","kindLabel":"obj","params":[],"preview":"window"},{"label":"document","type":"obj","kindLabel":"obj","params":[],"preview":"document"},{"label":"navigator","type":"obj","kindLabel":"obj","params":[],"preview":"navigator"},{"label":"console","type":"obj","kindLabel":"obj","params":[],"preview":"console","p5DocPath":"console"},{"label":"localStorage","type":"obj","kindLabel":"obj","params":[],"preview":"localStorage"},{"label":"sessionStorage","type":"obj","kindLabel":"obj","params":[],"preview":"sessionStorage"},{"label":"history","type":"obj","kindLabel":"obj","params":[],"preview":"history"},{"label":"location","type":"obj","kindLabel":"obj","params":[],"preview":"location"}];
diff --git a/client/utils/p5CodeAstAnalyzer.js b/client/utils/p5CodeAstAnalyzer.js
index 5aaa5ec7be..3307af8c5a 100644
--- a/client/utils/p5CodeAstAnalyzer.js
+++ b/client/utils/p5CodeAstAnalyzer.js
@@ -24,8 +24,7 @@ let lastValidResult = {
userDefinedClassMetadata: {}
};
-function _p5CodeAstAnalyzer(_cm) {
- const code = _cm.getValue();
+function _p5CodeAstAnalyzer(code) {
let ast;
try {
@@ -153,8 +152,7 @@ function _p5CodeAstAnalyzer(_cm) {
expr.left.object.type === 'ThisExpression' &&
expr.left.property.type === 'Identifier'
) {
- const propName = expr.left.property.name;
- classInfo.fields.add(propName);
+ classInfo.fields.add(expr.left.property.name);
}
},
@@ -165,8 +163,7 @@ function _p5CodeAstAnalyzer(_cm) {
callee.object.type === 'ThisExpression' &&
callee.property.type === 'Identifier'
) {
- const methodName = callee.property.name;
- classInfo.fields.add(methodName);
+ classInfo.fields.add(callee.property.name);
}
}
},
diff --git a/package-lock.json b/package-lock.json
index 7866a79d9e..0f7933c165 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "p5.js-web-editor",
- "version": "2.20.7",
+ "version": "2.20.8",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "p5.js-web-editor",
- "version": "2.20.7",
+ "version": "2.20.8",
"license": "LGPL-2.1",
"dependencies": {
"@auth0/s3": "^1.0.0",
@@ -26,7 +26,7 @@
"acorn": "^8.14.1",
"acorn-walk": "^8.3.4",
"async": "^3.2.3",
- "axios": "^1.13.5",
+ "axios": "^1.15.0",
"babel-plugin-styled-components": "^1.13.2",
"bcryptjs": "^2.4.3",
"blob-util": "^1.2.1",
@@ -66,13 +66,13 @@
"jsdom": "^20.0.0",
"jshint": "^2.13.0",
"jszip": "^3.10.1",
- "lodash": "^4.17.23",
+ "lodash": "^4.18.1",
"loop-protect": "github:catarak/loop-protect",
"mime": "^3.0.0",
"mjml": "^4.14.1",
"mongodb-memory-server": "^10.2.1",
"mongoose": "^8.16.3",
- "nodemailer": "^7.0.11",
+ "nodemailer": "^8.0.5",
"nodemailer-mailgun-transport": "^2.1.5",
"passport": "^0.6.0",
"passport-github2": "^0.1.12",
@@ -13497,10 +13497,11 @@
}
},
"node_modules/@storybook/core-common/node_modules/brace-expansion": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz",
+ "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
}
@@ -17325,14 +17326,14 @@
}
},
"node_modules/axios": {
- "version": "1.13.5",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz",
- "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==",
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz",
+ "integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.11",
"form-data": "^4.0.5",
- "proxy-from-env": "^1.1.0"
+ "proxy-from-env": "^2.1.0"
}
},
"node_modules/axobject-query": {
@@ -17997,9 +17998,10 @@
"integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA=="
},
"node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz",
+ "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==",
+ "license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -23315,9 +23317,9 @@
}
},
"node_modules/flatted": {
- "version": "3.4.1",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.1.tgz",
- "integrity": "sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==",
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
+ "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
"license": "ISC"
},
"node_modules/flatten": {
@@ -23913,10 +23915,11 @@
}
},
"node_modules/handlebars": {
- "version": "4.7.8",
- "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz",
- "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==",
+ "version": "4.7.9",
+ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz",
+ "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"minimist": "^1.2.5",
"neo-async": "^2.6.2",
@@ -27753,9 +27756,10 @@
}
},
"node_modules/js-beautify/node_modules/brace-expansion": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz",
+ "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==",
+ "license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
}
@@ -28027,6 +28031,12 @@
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
},
+ "node_modules/jshint/node_modules/lodash": {
+ "version": "4.17.23",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
+ "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
+ "license": "MIT"
+ },
"node_modules/jshint/node_modules/readable-stream": {
"version": "1.1.14",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
@@ -28473,9 +28483,9 @@
}
},
"node_modules/lodash": {
- "version": "4.17.23",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
- "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
+ "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
"license": "MIT"
},
"node_modules/lodash._reinterpolate": {
@@ -28522,10 +28532,12 @@
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
},
"node_modules/lodash.template": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz",
- "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==",
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.18.1.tgz",
+ "integrity": "sha512-5urZrLnV/VD6zHK5KsVtZgt7H19v51mIzoS0aBNH8yp3I8tbswrEjOABOPY8m8uB7NuibubLrMX+Y0PXsU9X+w==",
+ "deprecated": "This package is deprecated. Use https://socket.dev/npm/package/eta instead.",
"dev": true,
+ "license": "MIT",
"dependencies": {
"lodash._reinterpolate": "^3.0.0",
"lodash.templatesettings": "^4.0.0"
@@ -31309,9 +31321,9 @@
"integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw=="
},
"node_modules/nodemailer": {
- "version": "7.0.11",
- "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.11.tgz",
- "integrity": "sha512-gnXhNRE0FNhD7wPSCGhdNh46Hs6nm+uTyg+Kq0cZukNQiYdnCsoQjodNP9BQVG9XrcK/v6/MgpAPBUFyzh9pvw==",
+ "version": "8.0.5",
+ "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-8.0.5.tgz",
+ "integrity": "sha512-0PF8Yb1yZuQfQbq+5/pZJrtF6WQcjTd5/S4JOHs9PGFxuTqoB/icwuB44pOdURHJbRKX1PPoJZtY7R4VUoCC8w==",
"license": "MIT-0",
"engines": {
"node": ">=6.0.0"
@@ -32195,9 +32207,9 @@
}
},
"node_modules/path-to-regexp": {
- "version": "0.1.12",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
- "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz",
+ "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==",
"license": "MIT"
},
"node_modules/pathval": {
@@ -32226,9 +32238,10 @@
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
},
"node_modules/picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+ "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
+ "license": "MIT",
"engines": {
"node": ">=8.6"
},
@@ -33791,9 +33804,13 @@
}
},
"node_modules/proxy-from-env": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
- "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
+ "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
},
"node_modules/pseudomap": {
"version": "1.0.2",
@@ -38515,9 +38532,10 @@
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
},
"node_modules/yaml": {
- "version": "1.10.2",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
- "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+ "version": "1.10.3",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz",
+ "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==",
+ "license": "ISC",
"engines": {
"node": ">= 6"
}
@@ -48770,9 +48788,9 @@
}
},
"brace-expansion": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz",
+ "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==",
"dev": true,
"requires": {
"balanced-match": "^1.0.0"
@@ -51681,13 +51699,13 @@
"dev": true
},
"axios": {
- "version": "1.13.5",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz",
- "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==",
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz",
+ "integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==",
"requires": {
"follow-redirects": "^1.15.11",
"form-data": "^4.0.5",
- "proxy-from-env": "^1.1.0"
+ "proxy-from-env": "^2.1.0"
}
},
"axobject-query": {
@@ -52188,9 +52206,9 @@
"integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA=="
},
"brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz",
+ "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==",
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -56016,9 +56034,9 @@
}
},
"flatted": {
- "version": "3.4.1",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.1.tgz",
- "integrity": "sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ=="
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
+ "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA=="
},
"flatten": {
"version": "1.0.2",
@@ -56434,9 +56452,9 @@
"dev": true
},
"handlebars": {
- "version": "4.7.8",
- "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz",
- "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==",
+ "version": "4.7.9",
+ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz",
+ "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5",
@@ -59160,9 +59178,9 @@
},
"dependencies": {
"brace-expansion": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz",
+ "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==",
"requires": {
"balanced-match": "^1.0.0"
}
@@ -59369,6 +59387,11 @@
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
},
+ "lodash": {
+ "version": "4.17.23",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
+ "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w=="
+ },
"readable-stream": {
"version": "1.1.14",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
@@ -59711,9 +59734,9 @@
}
},
"lodash": {
- "version": "4.17.23",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
- "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w=="
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
+ "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q=="
},
"lodash._reinterpolate": {
"version": "3.0.0",
@@ -59759,9 +59782,9 @@
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
},
"lodash.template": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz",
- "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==",
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.18.1.tgz",
+ "integrity": "sha512-5urZrLnV/VD6zHK5KsVtZgt7H19v51mIzoS0aBNH8yp3I8tbswrEjOABOPY8m8uB7NuibubLrMX+Y0PXsU9X+w==",
"dev": true,
"requires": {
"lodash._reinterpolate": "^3.0.0",
@@ -61784,9 +61807,9 @@
"integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw=="
},
"nodemailer": {
- "version": "7.0.11",
- "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.11.tgz",
- "integrity": "sha512-gnXhNRE0FNhD7wPSCGhdNh46Hs6nm+uTyg+Kq0cZukNQiYdnCsoQjodNP9BQVG9XrcK/v6/MgpAPBUFyzh9pvw=="
+ "version": "8.0.5",
+ "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-8.0.5.tgz",
+ "integrity": "sha512-0PF8Yb1yZuQfQbq+5/pZJrtF6WQcjTd5/S4JOHs9PGFxuTqoB/icwuB44pOdURHJbRKX1PPoJZtY7R4VUoCC8w=="
},
"nodemailer-mailgun-transport": {
"version": "2.1.5",
@@ -62434,9 +62457,9 @@
}
},
"path-to-regexp": {
- "version": "0.1.12",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
- "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz",
+ "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA=="
},
"pathval": {
"version": "2.0.1",
@@ -62460,9 +62483,9 @@
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
},
"picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+ "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="
},
"pify": {
"version": "4.0.1",
@@ -63617,9 +63640,9 @@
}
},
"proxy-from-env": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
- "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
+ "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA=="
},
"pseudomap": {
"version": "1.0.2",
@@ -67093,9 +67116,9 @@
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
},
"yaml": {
- "version": "1.10.2",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
- "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="
+ "version": "1.10.3",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz",
+ "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA=="
},
"yargs": {
"version": "16.2.0",
diff --git a/package.json b/package.json
index f0ef304515..e8acfeca05 100644
--- a/package.json
+++ b/package.json
@@ -66,7 +66,12 @@
"displayName": "client",
"testEnvironment": "jsdom",
"transform": {
- "^.+\\.[jt]sx?$": "babel-jest"
+ "\\.[jt]sx?$": [
+ "babel-jest",
+ {
+ "configFile": "./.babelrc"
+ }
+ ]
},
"moduleFileExtensions": [
"ts",
@@ -83,6 +88,9 @@
"^.+\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)(|\\?byContent|\\?byUrl)$": "/client/__mocks__/fileMock.js",
"\\.(css|less|scss)$": "/client/__mocks__/styleMock.js"
},
+ "transformIgnorePatterns": [
+ "/node_modules/(?!colors-named|colors-named-hex|hsl-matcher|@emmetio/)"
+ ],
"testMatch": [
"/client/**/*.test.(js|jsx|ts|tsx)"
]
@@ -145,6 +153,7 @@
"@types/nodemailer": "^7.0.1",
"@types/nodemailer-mailgun-transport": "^1.4.6",
"@types/passport": "^1.0.17",
+ "@types/prettier": "^2.7.3",
"@types/react": "^16.14.0",
"@types/react-dom": "^16.9.25",
"@types/react-helmet": "^6.1.11",
@@ -194,7 +203,7 @@
"storybook-addon-theme-playground": "^3.1.0",
"style-loader": "^3.3.4",
"terser-webpack-plugin": "^5.3.1",
- "typescript": "^5.8.3",
+ "typescript": "^5.1.6",
"webpack-cli": "^4.9.2",
"webpack-manifest-plugin": "^5.0.0",
"webpack-node-externals": "^3.0.0"
@@ -210,8 +219,23 @@
"@babel/parser": "^7.27.5",
"@babel/register": "^7.14.5",
"@babel/traverse": "^7.27.4",
+ "@codemirror/autocomplete": "^6.18.6",
+ "@codemirror/commands": "^6.8.1",
+ "@codemirror/lang-css": "^6.3.1",
+ "@codemirror/lang-html": "^6.4.9",
+ "@codemirror/lang-javascript": "^6.2.3",
+ "@codemirror/lang-json": "^6.0.1",
+ "@codemirror/lang-xml": "^6.1.0",
+ "@codemirror/language": "^6.11.0",
+ "@codemirror/lint": "^6.8.5",
+ "@codemirror/search": "^6.5.11",
+ "@codemirror/state": "^6.5.2",
+ "@codemirror/view": "^6.40.0",
+ "@connieye/codemirror-color-picker": "^1.0.3",
"@emmetio/codemirror-plugin": "^1.2.4",
+ "@emmetio/codemirror6-plugin": "^0.4.0",
"@gatsbyjs/webpack-hot-middleware": "^2.25.3",
+ "@lezer/highlight": "^1.2.3",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.11",
"@redux-devtools/core": "^3.11.0",
"@redux-devtools/dock-monitor": "^3.0.1",
@@ -229,8 +253,7 @@
"bson-objectid": "^2.0.3",
"classnames": "^2.3.1",
"clipboard": "^1.7.1",
- "codemirror": "^5.62.0",
- "codemirror-colorpicker": "^1.9.72",
+ "codemirror": "^6.0.1",
"connect-mongo": "^5.1.0",
"console-feed": "^3.2.0",
"cookie-parser": "^1.4.5",
@@ -243,6 +266,7 @@
"dotenv": "^2.0.0",
"dropzone": "^4.3.0",
"escape-string-regexp": "^1.0.5",
+ "eslint-linter-browserify": "^10.0.3",
"eslint-scope": "^8.4.0",
"eslint-webpack-plugin": "^3.1.1",
"express": "^4.22.1",
@@ -252,7 +276,7 @@
"friendly-words": "^1.2.1",
"fuse.js": "^6.6.2",
"history": "^4.10.1",
- "htmlhint": "^0.15.1",
+ "htmlhint": "^0.15.2",
"i18next": "^19.9.2",
"i18next-http-backend": "^1.2.6",
"is-url": "^1.2.4",
diff --git a/server/controllers/collection.controller/listCollections.js b/server/controllers/collection.controller/listCollections.js
index e1ee11d28f..ead375c5b1 100644
--- a/server/controllers/collection.controller/listCollections.js
+++ b/server/controllers/collection.controller/listCollections.js
@@ -19,10 +19,34 @@ export default async function listCollections(req, res) {
res.status(code).json({ success: false, message });
};
- const sendSuccess = (collections) => {
- res.status(200).json(collections);
+ const sendSuccess = (payload) => {
+ res.status(200).json(payload);
};
+ const parsePositiveInt = (value, fallback) => {
+ const parsed = Number.parseInt(String(value), 10);
+ if (Number.isFinite(parsed) && parsed > 0) return parsed;
+ return fallback;
+ };
+
+ const coerceSortDir = (value) => {
+ const v = String(value || '').toLowerCase();
+ return v === 'asc' ? 'asc' : 'desc';
+ };
+
+ const coerceSortField = (value) => {
+ const allowed = new Set(['updatedAt', 'createdAt', 'name']);
+ const v = String(value || '');
+ return allowed.has(v) ? v : 'updatedAt';
+ };
+
+ const shouldPaginate = () =>
+ typeof req.query.page !== 'undefined' ||
+ typeof req.query.limit !== 'undefined' ||
+ typeof req.query.sortField !== 'undefined' ||
+ typeof req.query.sortDir !== 'undefined' ||
+ typeof req.query.q !== 'undefined';
+
try {
const ownerId = await getOwnerUserId(req);
@@ -30,37 +54,85 @@ export default async function listCollections(req, res) {
return sendFailure({ code: 404, message: 'User not found' });
}
- const collections = await Collection.find({ owner: ownerId }).populate([
+ const page = parsePositiveInt(req.query.page, 1);
+ const limit = parsePositiveInt(req.query.limit, 10);
+ const sortField = coerceSortField(req.query.sortField);
+ const sortDir = coerceSortDir(req.query.sortDir);
+ const q = String(req.query.q || '').trim();
+
+ const query = { owner: ownerId };
+ if (q) {
+ query.name = { $regex: q, $options: 'i' };
+ }
+
+ const baseFind = Collection.find(query).populate([
{ path: 'owner', select: ['id', 'username'] },
{
path: 'items.project',
select: ['id', 'name', 'slug', 'visibility'],
- populate: {
- path: 'user',
- select: ['username']
- }
+ populate: { path: 'user', select: ['username'] }
}
]);
const isOwner = req.user && req.user._id.equals(ownerId);
- if (isOwner) {
- return sendSuccess(collections);
+ if (!shouldPaginate()) {
+ const collections = await baseFind.exec();
+
+ if (isOwner) {
+ return sendSuccess(collections);
+ }
+
+ const publicCollections = collections.map((collection) => {
+ const { items: originalItems } = collection;
+ const items = originalItems.filter(
+ (item) => item.project && item.project.visibility === 'Public'
+ );
+ return {
+ ...collection.toObject(),
+ items,
+ id: collection._id
+ };
+ });
+
+ return sendSuccess(publicCollections);
}
- const publicCollections = collections.map((collection) => {
- const { items: originalItems } = collection;
- const items = originalItems.filter(
- (item) => item.project && item.project.visibility === 'Public'
- );
- return {
- ...collection.toObject(),
- items,
- id: collection._id
- };
- });
+ const totalCollections = await Collection.countDocuments(query);
+ const totalPages = Math.max(1, Math.ceil(totalCollections / limit));
+ const safePage = Math.min(page, totalPages);
+ const skip = (safePage - 1) * limit;
- return sendSuccess(publicCollections);
+ const collections = await baseFind
+ .sort({ [sortField]: sortDir })
+ .skip(skip)
+ .limit(limit)
+ .exec();
+
+ const normalizedCollections = isOwner
+ ? collections
+ : collections.map((collection) => {
+ const { items: originalItems } = collection;
+ const items = originalItems.filter(
+ (item) => item.project && item.project.visibility === 'Public'
+ );
+ return {
+ ...collection.toObject(),
+ items,
+ id: collection._id
+ };
+ });
+
+ return sendSuccess({
+ collections: normalizedCollections,
+ metadata: {
+ page: safePage,
+ totalPages,
+ totalCollections,
+ limit,
+ hasPagination: totalPages > 1
+ }
+ });
} catch (error) {
return sendFailure({
code: error.code || 500,
diff --git a/server/scripts/update-p5-hinter.js b/server/scripts/update-p5-hinter.js
index a6a667eac4..d438c28ca0 100644
--- a/server/scripts/update-p5-hinter.js
+++ b/server/scripts/update-p5-hinter.js
@@ -2,81 +2,90 @@ const fs = require('fs');
const process = require('process');
const axios = require('axios');
-// const getDescription = (d) => {
-// return d.split('\n')[0].replace('', '');
-// };
-
+// TODO: Currently this makes duplicate entries because
+// the default Javascript hinter also has these,
+// but should we keep them around for the p5 reference links?
const reservedKeywords = [
- { name: 'await', p5DocPath: false },
- { name: 'break', p5DocPath: false },
- { name: 'case', p5DocPath: false },
- { name: 'catch', p5DocPath: false },
+ { name: 'await', p5DocPath: undefined },
{ name: 'class', p5DocPath: 'class' },
{ name: 'const', p5DocPath: 'const' },
- { name: 'continue', p5DocPath: false },
- { name: 'debugger', p5DocPath: false },
- { name: 'default', p5DocPath: false },
- { name: 'delete', p5DocPath: false },
- { name: 'do', p5DocPath: false },
{ name: 'else', p5DocPath: 'if-else' },
- { name: 'export', p5DocPath: false },
- { name: 'extends', p5DocPath: false },
- { name: 'finally', p5DocPath: false },
+ { name: 'export', p5DocPath: undefined },
{ name: 'for', p5DocPath: 'for' },
{ name: 'function', p5DocPath: 'function' },
{ name: 'if', p5DocPath: 'if-else' },
- { name: 'import', p5DocPath: false },
- { name: 'in', p5DocPath: false },
- { name: 'instanceof', p5DocPath: false },
- { name: 'new', p5DocPath: false },
{ name: 'return', p5DocPath: 'return' },
- { name: 'super', p5DocPath: false },
- { name: 'switch', p5DocPath: false },
- { name: 'this', p5DocPath: false },
- { name: 'throw', p5DocPath: false },
- { name: 'try', p5DocPath: false },
- { name: 'typeof', p5DocPath: false },
- { name: 'var', p5DocPath: false },
- { name: 'void', p5DocPath: false },
{ name: 'while', p5DocPath: 'while' },
- { name: 'with', p5DocPath: false },
- { name: 'yield', p5DocPath: false },
+ { name: 'with', p5DocPath: undefined },
{ name: 'let', p5DocPath: 'let' }
];
const reservedObjects = [
- { name: 'Array', p5DocPath: false },
- { name: 'Boolean', p5DocPath: false },
- { name: 'Date', p5DocPath: false },
- { name: 'Error', p5DocPath: false },
- { name: 'Function', p5DocPath: false },
+ { name: 'Array', p5DocPath: undefined },
+ { name: 'Boolean', p5DocPath: undefined },
+ { name: 'Date', p5DocPath: undefined },
+ { name: 'Error', p5DocPath: undefined },
+ { name: 'Function', p5DocPath: undefined },
{ name: 'JSON', p5DocPath: 'JSON' },
- { name: 'Math', p5DocPath: false },
- { name: 'Number', p5DocPath: false },
- { name: 'Object', p5DocPath: false },
- { name: 'RegExp', p5DocPath: false },
- { name: 'String', p5DocPath: false },
- { name: 'Promise', p5DocPath: false },
- { name: 'Set', p5DocPath: false },
- { name: 'Map', p5DocPath: false },
- { name: 'Symbol', p5DocPath: false },
- { name: 'WeakMap', p5DocPath: false },
- { name: 'WeakSet', p5DocPath: false },
- { name: 'ArrayBuffer', p5DocPath: false },
- { name: 'DataView', p5DocPath: false },
- { name: 'Int32Array', p5DocPath: false },
- { name: 'Uint32Array', p5DocPath: false },
- { name: 'Float32Array', p5DocPath: false },
- { name: 'window', p5DocPath: false },
- { name: 'document', p5DocPath: false },
- { name: 'navigator', p5DocPath: false },
+ { name: 'Math', p5DocPath: undefined },
+ { name: 'Number', p5DocPath: undefined },
+ { name: 'Object', p5DocPath: undefined },
+ { name: 'RegExp', p5DocPath: undefined },
+ { name: 'String', p5DocPath: undefined },
+ { name: 'Promise', p5DocPath: undefined },
+ { name: 'Set', p5DocPath: undefined },
+ { name: 'Map', p5DocPath: undefined },
+ { name: 'Symbol', p5DocPath: undefined },
+ { name: 'WeakMap', p5DocPath: undefined },
+ { name: 'WeakSet', p5DocPath: undefined },
+ { name: 'ArrayBuffer', p5DocPath: undefined },
+ { name: 'DataView', p5DocPath: undefined },
+ { name: 'Int32Array', p5DocPath: undefined },
+ { name: 'Uint32Array', p5DocPath: undefined },
+ { name: 'Float32Array', p5DocPath: undefined },
+ { name: 'window', p5DocPath: undefined },
+ { name: 'document', p5DocPath: undefined },
+ { name: 'navigator', p5DocPath: undefined },
{ name: 'console', p5DocPath: 'console' },
- { name: 'localStorage', p5DocPath: false },
- { name: 'sessionStorage', p5DocPath: false },
- { name: 'history', p5DocPath: false },
- { name: 'location', p5DocPath: false }
+ { name: 'localStorage', p5DocPath: undefined },
+ { name: 'sessionStorage', p5DocPath: undefined },
+ { name: 'history', p5DocPath: undefined },
+ { name: 'location', p5DocPath: undefined }
];
+function getKindLabel(type) {
+ switch (type) {
+ case 'method':
+ return 'fun';
+ case 'variable':
+ return 'var';
+ case 'constant':
+ return 'const';
+ case 'keyword':
+ return 'kw';
+ case 'boolean':
+ return 'bool';
+ case 'obj':
+ return 'obj';
+ default:
+ return type;
+ }
+}
+
+// create ghost text preview for methods
+function makePreview(label, type, params = []) {
+ const formattedParams = params
+ .map((param) => (param.o ? `[${param.p}]` : param.p))
+ .join(', ');
+
+ if (type === 'method') {
+ return `${label}(${formattedParams})`;
+ }
+
+ return label;
+}
+
+// TODO: add back in reference version switching depending user's p5.js version
axios
.get('https://p5js.org/reference/data.json')
.then((response) => {
@@ -92,49 +101,60 @@ axios
obj.name &&
obj.itemtype
) {
- let type;
+ let itemType;
let params = [];
if (obj.itemtype === 'method') {
- type = 'fun';
+ itemType = 'method';
params = obj.params?.map((param) => ({
p: param.name, // param name
o: param.optional ?? false // optional
}));
} else if (obj.itemtype === 'property') {
- type = 'var';
- } else type = 'attr';
+ itemType = obj.module === 'Constants' ? 'constant' : 'variable';
+ } else itemType = 'attr';
p5Keywords.push({
- text: obj.name,
- type,
+ label: obj.name,
+ type: itemType,
+ kindLabel: getKindLabel(itemType),
params,
- p5: true
+ preview: makePreview(obj.name, itemType, params),
+ p5DocPath: obj.name
});
}
});
['true', 'false'].forEach((bol) => {
p5Keywords.push({
- text: bol,
+ label: bol,
type: 'boolean',
- p5: 'boolean'
+ kindLabel: 'bool',
+ params: [],
+ preview: bol,
+ p5DocPath: 'boolean'
});
});
reservedKeywords.forEach((keyword) => {
p5Keywords.push({
- text: keyword.name,
+ label: keyword.name,
type: 'keyword',
- p5: keyword.p5DocPath
+ kindLabel: 'keyword',
+ params: [],
+ preview: keyword.name,
+ p5DocPath: keyword.p5DocPath
});
});
reservedObjects.forEach((keyword) => {
p5Keywords.push({
- text: keyword.name,
+ label: keyword.name,
type: 'obj',
- p5: keyword.p5DocPath
+ kindLabel: 'obj',
+ params: [],
+ preview: keyword.name,
+ p5DocPath: keyword.p5DocPath
});
});
diff --git a/translations/locales/bn/translations.json b/translations/locales/bn/translations.json
index 613fdb67b2..8aa2fc4786 100644
--- a/translations/locales/bn/translations.json
+++ b/translations/locales/bn/translations.json
@@ -169,6 +169,7 @@
"Settings": "সেটিংস",
"GeneralSettings": "সাধারণ সেটিংস",
"Accessibility": "ব্যবহারযোগ্যতা",
+ "LibraryManagement": "লাইব্রেরি ম্যানেজমেন্ট",
"Theme": "থিম",
"LightTheme": "সাদা",
"LightThemeARIA": "সাদা থিম চালু",
@@ -472,6 +473,7 @@
"AddSketch": "স্কেচ যোগ করুন",
"DeleteFromCollection": "{{name_sketch}} কে এই সংগ্রহ থেকে সরাতে চান কি না?",
"SketchDeleted": "স্কেচ ডিলিট হয়েছে",
+ "SketchRemoveLabel": "Remove from Collection",
"SketchRemoveARIA": "সংগ্রহ থেকে স্কেচ রিমুভ করুন",
"DescriptionPlaceholder": "বর্ণনা যোগ করুন",
"NumSketches": "{{count}} স্কেচ",
diff --git a/translations/locales/de/translations.json b/translations/locales/de/translations.json
index b05ed9c6ba..d05907f1ce 100644
--- a/translations/locales/de/translations.json
+++ b/translations/locales/de/translations.json
@@ -432,6 +432,7 @@
"AddSketch": "Sketch hinzufügen",
"DeleteFromCollection": "Bist Du sicher, dass Du {{name_sketch}} aus dieser Sammlung entfernen willst?",
"SketchDeleted": "Sketch gelöscht",
+ "SketchRemoveLabel": "Remove from Collection",
"SketchRemoveARIA": "Sketch aus der Sammlung entfernen",
"DescriptionPlaceholder": "Beschreibung hinzufügen",
"Description": "Beschreibung",
diff --git a/translations/locales/en-US/translations.json b/translations/locales/en-US/translations.json
index 7529a1d9ff..a1c17007c8 100644
--- a/translations/locales/en-US/translations.json
+++ b/translations/locales/en-US/translations.json
@@ -516,6 +516,7 @@
"DeleteFromCollection": "Are you sure you want to remove {{name_sketch}} from this collection?",
"SketchDeleted": "Sketch deleted",
"SketchRemoveARIA": "Remove sketch from collection",
+ "SketchRemoveLabel": "Remove from Collection",
"DescriptionPlaceholder": "Add description",
"Description": "description",
"NumSketches": "{{count}} sketch",
diff --git a/translations/locales/es-419/translations.json b/translations/locales/es-419/translations.json
index 3880322a65..4a7e80dbcc 100644
--- a/translations/locales/es-419/translations.json
+++ b/translations/locales/es-419/translations.json
@@ -442,6 +442,7 @@
"AddSketch": "Agregar Bosquejo",
"DeleteFromCollection": "¿Estás seguro que quieres remover {{name_sketch}} de esta colección?",
"SketchDeleted": "El bosquejo fue eliminado",
+ "SketchRemoveLabel": "Remove from Collection",
"SketchRemoveARIA": "Remover bosquejo de la colección",
"DescriptionPlaceholder": "Agregar descripción",
"Description": "descripción",
diff --git a/translations/locales/fr-CA/translations.json b/translations/locales/fr-CA/translations.json
index dab5114c54..50730b6c3c 100644
--- a/translations/locales/fr-CA/translations.json
+++ b/translations/locales/fr-CA/translations.json
@@ -446,6 +446,7 @@
"AddSketch": "Ajouter un croquis",
"DeleteFromCollection": "Êtes-vous sûr de vouloir supprimer {{name_sketch}} de cette collection?",
"SketchDeleted": "Croquis supprimé",
+ "SketchRemoveLabel": "Remove from Collection",
"SketchRemoveARIA": "Supprimer le croquis de la collection",
"DescriptionPlaceholder": "Ajouter une description",
"Description": "description",
diff --git a/translations/locales/hi/translations.json b/translations/locales/hi/translations.json
index 49f4af1515..bb0355cbf0 100644
--- a/translations/locales/hi/translations.json
+++ b/translations/locales/hi/translations.json
@@ -226,10 +226,10 @@
"TableOutputARIA": "टेबल आउटपुट चालू",
"LibraryVersion": "p5.js संस्करण",
"LibraryVersionInfo": "p5.js का एक [नया 2.0 संस्करण](https://github.com/processing/p5.js/releases/) उपलब्ध है! यह अगस्त 2026 में डिफ़ॉल्ट बन जाएगा, इसलिए इस समय का उपयोग इसे आज़माने और बग्स की रिपोर्ट करने के लिए करें। क्या आप 1.x से 2.0 में स्केच को स्थानांतरित करने में रुचि रखते हैं? [संगतता और स्थानांतरण संसाधनों](https://github.com/processing/p5.js-compatibility) को देखें।",
- "SoundAddon": "p5.sound.js Add-on लाइब्रेरी",
- "PreloadAddon": "p5.js 1.x Compatibility Add-on लाइब्रेरी — प्रीलोड",
- "ShapesAddon": "p5.js 1.x Compatibility Add-on लाइब्रेरी — आकार",
- "DataAddon": "p5.js 1.x Compatibility Add-on लाइब्रेरी — डेटा संरचनाएँ"
+ "SoundAddon": "p5.sound.js ऐड-ऑन लाइब्रेरी",
+ "PreloadAddon": "p5.js 1.x संगतता ऐड-ऑन लाइब्रेरी — प्रीलोड",
+ "ShapesAddon": "p5.js 1.x संगतता ऐड-ऑन लाइब्रेरी — आकार",
+ "DataAddon": "p5.js 1.x संगतता ऐड-ऑन लाइब्रेरी — डेटा संरचनाएँ"
},
"KeyboardShortcuts": {
"Title": " कीबोर्ड शॉर्टकट",
@@ -510,6 +510,7 @@
"AddSketch": "स्केच जोड़ें",
"DeleteFromCollection": "क्या आप वाकई {{name_sketch}} को इस संग्रह से हटाना चाहते हो?",
"SketchDeleted": "स्केच डिलीट किया",
+ "SketchRemoveLabel": "Remove from Collection",
"SketchRemoveARIA": "स्केच संग्रह से हटाएं",
"DescriptionPlaceholder": "विवरण लिखें",
"NumSketches": "{{count}} स्केच",
diff --git a/translations/locales/it/translations.json b/translations/locales/it/translations.json
index 4dfb4ac25a..1ff103e66c 100644
--- a/translations/locales/it/translations.json
+++ b/translations/locales/it/translations.json
@@ -445,6 +445,7 @@
"AddSketch": "Aggiungi sketch",
"DeleteFromCollection": "Sei sicuro di voler cancellare {{name_sketch}} dalla collezione?",
"SketchDeleted": "Sketch cancellato",
+ "SketchRemoveLabel": "Remove from Collection",
"SketchRemoveARIA": "Cancella sketch dalla collezione",
"DescriptionPlaceholder": "Aggiungi descrizione",
"Description": "descrizione",
diff --git a/translations/locales/ja/translations.json b/translations/locales/ja/translations.json
index eda22c412e..5200507190 100644
--- a/translations/locales/ja/translations.json
+++ b/translations/locales/ja/translations.json
@@ -436,6 +436,7 @@
"AddSketch": "スケッチを追加する",
"DeleteFromCollection": "このコレクションから {{name_sketch}} を削除してもよろしいですか?",
"SketchDeleted": "スケッチが削除されました",
+ "SketchRemoveLabel": "Remove from Collection",
"SketchRemoveARIA": "コレクションからスケッチを削除する",
"DescriptionPlaceholder": "スケッチについて記述する",
"Description": "コレクションについて",
diff --git a/translations/locales/ko/translations.json b/translations/locales/ko/translations.json
index 7f915a71ff..972e048a8d 100644
--- a/translations/locales/ko/translations.json
+++ b/translations/locales/ko/translations.json
@@ -424,6 +424,7 @@
"AddSketch": "스케치 추가하기 Add Sketch",
"DeleteFromCollection": "Are you sure you want to remove {{name_sketch}} from this collection?",
"SketchDeleted": "Sketch deleted",
+ "SketchRemoveLabel": "Remove from Collection",
"SketchRemoveARIA": "Remove sketch from collection",
"DescriptionPlaceholder": "Add description",
"Description": "description",
diff --git a/translations/locales/ne/translations.json b/translations/locales/ne/translations.json
index 4d61693c08..f0306847ec 100644
--- a/translations/locales/ne/translations.json
+++ b/translations/locales/ne/translations.json
@@ -507,6 +507,7 @@
"AddSketch": "स्केच थप्नुहोस्",
"DeleteFromCollection": "के तपाईं पक्का यो कलेक्सनबाट {{name_sketch}} हटाउन चाहनुहुन्छ?",
"SketchDeleted": "स्केच डिलिट भयो",
+ "SketchRemoveLabel": "Remove from Collection",
"SketchRemoveARIA": "कलेक्सनबाट स्केच हटाउनुहोस्",
"DescriptionPlaceholder": "विवरण थप्नुहोस्",
"Description": "विवरण",
diff --git a/translations/locales/pt-BR/translations.json b/translations/locales/pt-BR/translations.json
index 2fdfd4bef1..df49836ae9 100644
--- a/translations/locales/pt-BR/translations.json
+++ b/translations/locales/pt-BR/translations.json
@@ -475,6 +475,7 @@
"AddSketch": "Adicionar Esboço",
"DeleteFromCollection": "Realmente quer remover {{name_sketch}} dessa coleção?",
"SketchDeleted": "Esboço apagado",
+ "SketchRemoveLabel": "Remove from Collection",
"SketchRemoveARIA": "Remover esboço da coleção",
"DescriptionPlaceholder": "Adicionar descrição",
"NumSketches": "{{count}} esboço(s)",
diff --git a/translations/locales/sv/translations.json b/translations/locales/sv/translations.json
index e72edd835f..b96a08d3ed 100644
--- a/translations/locales/sv/translations.json
+++ b/translations/locales/sv/translations.json
@@ -435,6 +435,7 @@
"AddSketch": "Lägg till sketch",
"DeleteFromCollection": "Är du säker på att du vill radera {{name_sketch}} från den här samlingen?",
"SketchDeleted": "Sketchen är raderad",
+ "SketchRemoveLabel": "Remove from Collection",
"SketchRemoveARIA": "Radera sketch från samling",
"DescriptionPlaceholder": "Lägg till beskrivning",
"Description": "beskrivning",
diff --git a/translations/locales/tr/translations.json b/translations/locales/tr/translations.json
index c66c561e68..8eb599f91f 100644
--- a/translations/locales/tr/translations.json
+++ b/translations/locales/tr/translations.json
@@ -439,6 +439,7 @@
"AddSketch": "Eskiz ekle",
"DeleteFromCollection": "{{name_sketch}} eskizini koleksiyondan kaldırmak istediğinizden emin misiniz?",
"SketchDeleted": "Eskiz silindi",
+ "SketchRemoveLabel": "Remove from Collection",
"SketchRemoveARIA": "Eskiz koleksiyondan kaldır",
"DescriptionPlaceholder": "Açıklama ekle",
"Description": "açıklama",
diff --git a/translations/locales/uk-UA/translations.json b/translations/locales/uk-UA/translations.json
index b254d11387..9101e500d3 100644
--- a/translations/locales/uk-UA/translations.json
+++ b/translations/locales/uk-UA/translations.json
@@ -497,6 +497,7 @@
"AddSketch": "Додати скетч",
"DeleteFromCollection": "Ви впевнені, що хочете видалити {{name_sketch}} з цієї колекції?",
"SketchDeleted": "Скетч видалено",
+ "SketchRemoveLabel": "Remove from Collection",
"SketchRemoveARIA": "Видалити скетч із колекції",
"DescriptionPlaceholder": "Додати опис",
"Description": "опис",
diff --git a/translations/locales/ur/translations.json b/translations/locales/ur/translations.json
index c6f90d5abf..85e8f0b6cf 100644
--- a/translations/locales/ur/translations.json
+++ b/translations/locales/ur/translations.json
@@ -436,6 +436,7 @@
"AddSketch": "خاکہ شامل کریں۔",
"DeleteFromCollection": "کیا آپ واقعی ہٹانا چاہتے ہیں {{name_sketch}} اس مجموعہ سے؟",
"SketchDeleted": "خاکہ حذف کر دیا گیا۔",
+ "SketchRemoveLabel": "Remove from Collection",
"SketchRemoveARIA": "مجموعہ سے خاکہ کو ہٹا دیں۔",
"DescriptionPlaceholder": "تفصیل شامل کریں۔",
"Description": "تفصیل",
diff --git a/translations/locales/zh-CN/translations.json b/translations/locales/zh-CN/translations.json
index ce5e6cd027..d95a20f2e9 100644
--- a/translations/locales/zh-CN/translations.json
+++ b/translations/locales/zh-CN/translations.json
@@ -438,6 +438,7 @@
"AddSketch": "添加项目",
"DeleteFromCollection": "您确定要从此集合中删除 {{name_sketch}} 吗?",
"SketchDeleted": "项目已删除",
+ "SketchRemoveLabel": "Remove from Collection",
"SketchRemoveARIA": "从集合中移除项目",
"DescriptionPlaceholder": "添加描述",
"Description": "描述",
diff --git a/translations/locales/zh-TW/translations.json b/translations/locales/zh-TW/translations.json
index ec501e52ad..d6138fcbb7 100644
--- a/translations/locales/zh-TW/translations.json
+++ b/translations/locales/zh-TW/translations.json
@@ -438,6 +438,7 @@
"AddSketch": "新增草稿",
"DeleteFromCollection": "確定要從本作品集移除 {{name_sketch}}?",
"SketchDeleted": "已刪除草稿",
+ "SketchRemoveLabel": "Remove from Collection",
"SketchRemoveARIA": "從作品集移除草稿",
"DescriptionPlaceholder": "新增說明",
"Description": "說明",