diff --git a/.bundlewatch.config.js b/.bundlewatch.config.js index 4177c011e..f87095cd9 100644 --- a/.bundlewatch.config.js +++ b/.bundlewatch.config.js @@ -18,12 +18,12 @@ module.exports = { // }, { path: './build/livecodes/*.css', - maxSize: '15kB', - }, - { - path: './build/livecodes/i18n-*.json', - maxSize: '10kB', + maxSize: '25kB', }, + // { + // path: './build/livecodes/i18n-*.json', + // maxSize: '10kB', + // }, ], defaultCompression: 'brotli', normalizeFilenames: /^.+?((\.[^.]{8,}}?)|())\.\w+$/, diff --git a/README.md b/README.md index b7c70a517..9f905c29e 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # LiveCodes -Code Playground That Just Works! +A Code Playground That Just Works!

@@ -30,7 +30,7 @@ A [feature-rich](https://livecodes.io/docs/features/), open-source, **client-sid ![LiveCodes list of languages screenshot](https://dev.livecodes.io/docs/img/screenshots/livecodes-languages.jpg) -## Code Playground That Just Works! +## A Code Playground That Just Works! - No servers to configure (or pay for!) - No databases to maintain (or pay for!) diff --git a/docs/docs/about.md b/docs/docs/about.md index 789608b1b..d3d9cb2cb 100644 --- a/docs/docs/about.md +++ b/docs/docs/about.md @@ -1,6 +1,6 @@ # About us -LiveCodes is built and maintained primarily by [Hatem Hosny](https://github.com/hatemhosny), from Egypt Egypt. +LiveCodes is built and maintained by [Hatem Hosny](https://github.com/hatemhosny), and wonderful [contributors](https://github.com/live-codes/livecodes/graphs/contributors). Feature requests and bug reports are received on the [GitHub repo](https://github.com/live-codes/livecodes/issues). diff --git a/docs/docs/configuration/configuration-object.md b/docs/docs/configuration/configuration-object.md index 4f00842fa..aad6b1acb 100644 --- a/docs/docs/configuration/configuration-object.md +++ b/docs/docs/configuration/configuration-object.md @@ -59,7 +59,7 @@ Type: [`string`](../api/interfaces/Config.md#title) Default: `"Untitled Project"` -Project title. This is used as [result page](../features/result.md) title and title meta tag. Also used in [project](../features/projects.md) search. This can be set in the UI from the title input (above result page) or from app menu → Project Info. +Project title. This is used as [result page](../features/result.md) title and title meta tag. Also used in [project](../features/projects.md) search. This can be set in the UI from the title input (above result page) or from Project menu → Project Info. ### `description` @@ -67,7 +67,7 @@ Type: [`string`](../api/interfaces/Config.md#description) Default: `""` -Project description. Used in [project](../features/projects.md) search and result page description meta tag. This can be set in the UI from app menu → Project Info. +Project description. Used in [project](../features/projects.md) search and result page description meta tag. This can be set in the UI from Project menu → Project Info. ### `head` @@ -75,7 +75,7 @@ Type: [`string`](../api/interfaces/Config.md#head) Default: `'\n'` -Content added to the [result page](../features/result.md) `` element. This can be set in the UI from app menu → Project Info. +Content added to the [result page](../features/result.md) `` element. This can be set in the UI from Project menu → Project Info. ### `htmlAttrs` @@ -88,7 +88,7 @@ Attributes added to the [result page](../features/result.md) `` element. I Example: `{ lang: "en", class: "dark" }` or `'lang="en" class="dark"'`, become ``. -This can be set in the UI from app menu → Project Info. +This can be set in the UI from Project menu → Project Info. ### `tags` @@ -96,7 +96,7 @@ Type: [`string[]`](../api/interfaces/Config.md#tags) Default: `[]` -Project tags. Used in [project](../features/projects.md) filter and search. This can be set in the UI from app menu → Project Info. +Project tags. Used in [project](../features/projects.md) filter and search. This can be set in the UI from Project menu → Project Info. ### `activeEditor` @@ -114,7 +114,7 @@ Default: all supported languages in full app and only current editor languages i List of enabled languages. Languages that are not already loaded in the editors ([markup](#markup), [style](#style) and [script](#script)) can be selected from a drop down menu at the editor title. -![Change Language](../../static/img/screenshots/languages.jpg) +![Change Language](../../static/img/screenshots/languages-3.jpg) ### `markup` @@ -485,6 +485,14 @@ Default: `"dark"` Sets the app [theme](../features/themes.md) to light/dark mode. +### `themeColor` + +Type: [`string | undefined`](../api/interfaces/Config.md#themecolor) + +Default: `"hsl(214, 40%, 50%)"` + +A string representing a [CSS color value](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value), used to set the app [theme color](../features/themes.md). It can be any valid CSS color value, such as `"#4DB39E"`, `"rgb(245, 225, 49)"`, `"hsl(324, 40%, 50%)"` and `"lightblue"`. + ### `editorTheme` Type: [`EditorTheme[] | string | undefined`](../api/interfaces/Config.md#editortheme) diff --git a/docs/docs/configuration/query-params.md b/docs/docs/configuration/query-params.md index 580799db4..4ead11c07 100644 --- a/docs/docs/configuration/query-params.md +++ b/docs/docs/configuration/query-params.md @@ -37,11 +37,14 @@ https://livecodes.io?js=console.log('Hello World!')&console=open [delay](./configuration-object.md#delay), [formatOnsave](./configuration-object.md#formatOnsave), [theme](./configuration-object.md#theme), + [themeColor](./configuration-object.md#themecolor), [appLanguage](./configuration-object.md#applanguage), [recoverUnsaved](./configuration-object.md#recoverUnsaved), [welcome](./configuration-object.md#welcome), [showSpacing](./configuration-object.md#showSpacing), + [layout](./configuration-object.md#layout), [editor](./configuration-object.md#editor), + [editorTheme](./configuration-object.md#editortheme), [fontFamily](./configuration-object.md#fontFamily), [fontSize](./configuration-object.md#fontSize), [useTabs](./configuration-object.md#useTabs), diff --git a/docs/docs/features/ai.md b/docs/docs/features/ai.md index 8a1664a9d..7b32a7a60 100644 --- a/docs/docs/features/ai.md +++ b/docs/docs/features/ai.md @@ -32,13 +32,13 @@ The AI code assistant can be enabled from: ### UI -The [editor settings](./editor-settings.md) screen (App menu → Editor Settings → Enable AI Code Assistant). +The [editor settings](./editor-settings.md) screen (Settings menu → Editor Settings → Enable AI Code Assistant). import RunInLiveCodes from '../../src/components/RunInLiveCodes.tsx'; -![LiveCodes Editor Settings](../../static/img/screenshots/editor-settings-1.png) +![LiveCodes Editor Settings](../../static/img/screenshots/editor-settings-1.jpg) **Note** diff --git a/docs/docs/features/assets.md b/docs/docs/features/assets.md index e369c2371..15b939cdf 100644 --- a/docs/docs/features/assets.md +++ b/docs/docs/features/assets.md @@ -6,7 +6,9 @@ The assets are saved locally on the user's device and are available across proje In addition, assets are supported in [sync](./sync.md), [backup](./backup-restore.md#backup) and [restore](./backup-restore.md#restore). -The `Assets` screen can be accessed from app menu → Assets +The `Assets` screen can be accessed from Settings menu → Assets + +![Assets](/img/screenshots/assets-1.jpg) import RunInLiveCodes from '../../src/components/RunInLiveCodes.tsx'; @@ -21,4 +23,4 @@ Assets are either: When an asset item is clicked, the URL is copied to clipboard. The URL can then be used in projects. -![Assets](/img/screenshots/assets.png) +![Assets](/img/screenshots/assets-2.jpg) diff --git a/docs/docs/features/backup-restore.md b/docs/docs/features/backup-restore.md index ef41791e5..07e4e5f1f 100644 --- a/docs/docs/features/backup-restore.md +++ b/docs/docs/features/backup-restore.md @@ -2,7 +2,7 @@ LiveCodes data can be backed-up, so that it can be later restored on the same or different device. -The Backup/Restore screen can be accessed from the app menu → Backup / Restore. +The Backup/Restore screen can be accessed from the Settings menu → Backup / Restore. import RunInLiveCodes from '../../src/components/RunInLiveCodes.tsx'; @@ -10,7 +10,7 @@ import RunInLiveCodes from '../../src/components/RunInLiveCodes.tsx'; ## Backup -![LiveCode Backup](../../static/img/screenshots/backup.jpg) +![LiveCode Backup](../../static/img/screenshots/backup-restore-1.jpg) The backup can include one or more of the following: @@ -24,7 +24,7 @@ A zip file containing the (base64-encoded binary) backup data is downloaded. Thi ## Restore -![LiveCode Restore](../../static/img/screenshots/restore.jpg) +![LiveCode Restore](../../static/img/screenshots/backup-restore-2.jpg) When restoring a backup, there are 2 options for managing the current data: diff --git a/docs/docs/features/broadcast.md b/docs/docs/features/broadcast.md index 0d3607d2e..db6e36733 100644 --- a/docs/docs/features/broadcast.md +++ b/docs/docs/features/broadcast.md @@ -10,23 +10,23 @@ Broadcast can only be performed from the full app, and not from embedded playgro ::: -The `Broadcast` screen can be accessed from the Broadcast icon in the [tools pane](./tools-pane.md) (below the result page), or from the app menu → Broadcast. +The `Broadcast` screen can be accessed from the Broadcast icon in the [tools pane](./tools-pane.md) (below the result page), or from the Project menu → Broadcast. import RunInLiveCodes from '../../src/components/RunInLiveCodes.tsx'; -![Broadcast UI](./../../static/img/screenshots/broadcast.jpg) +![Broadcast UI](./../../static/img/screenshots/broadcast-1.jpg) On connecting to the server, the channel URL returned by the server is displayed. The channel URL can be shared to different clients (browsers on same or different devices) to view result page or code updates in real-time. If the option `Include source code` is enabled, the source and compiled code together with the current project configuration are also posted to the server with each update. -![Broadcast UI - broadcasting](./../../static/img/screenshots/broadcasting.jpg) +![Broadcast UI - broadcasting](./../../static/img/screenshots/broadcast-2.jpg) The Broadcast icon (in tools pane), shows the broadcast status. Clicking the icon, opens the broadcast UI, where the channel URL is displayed and the broadcast can be stopped. -![Broadcast icon - broadcasting](./../../static/img/screenshots/broadcasting2.jpg) +![Broadcast icon - broadcasting](./../../static/img/screenshots/broadcast-3.jpg) :::info Server @@ -57,9 +57,9 @@ You can use one of these links to self-host it: These are screenshots for the live-updated result page and code: -![Broadcasting result](./../../static/img/screenshots/broadcasting-result.jpg) +![Broadcasting result](./../../static/img/screenshots/broadcast-4.jpg) -![Broadcasting code](./../../static/img/screenshots/broadcasting-code.jpg) +![Broadcasting code](./../../static/img/screenshots/broadcast-5.jpg) ## Technical Details diff --git a/docs/docs/features/code-format.md b/docs/docs/features/code-format.md index 1d900633a..11887af10 100644 --- a/docs/docs/features/code-format.md +++ b/docs/docs/features/code-format.md @@ -16,7 +16,7 @@ For example: Code formatting for the code in the active editor can be triggered by the `Format` button below the editor. -![code format](../../static/img/screenshots/format.jpg) +![code format](../../static/img/screenshots/format-1.jpg) ## Keyboard Shortcut @@ -24,7 +24,7 @@ Code formatting can also be trigger by the keyboard shortcut Alt + sets TypeScript as the active editor and shows compiled code viewer maximized. diff --git a/docs/docs/features/css.md b/docs/docs/features/css.md index a0838b226..8dbb81dd2 100644 --- a/docs/docs/features/css.md +++ b/docs/docs/features/css.md @@ -8,7 +8,7 @@ The [result page](./result.md) can be styled with CSS using various methods, inc Code added to [style editor](./projects.md#style-editor) is [compiled](#languages) and [processed](#css-processors), then added as CSS to the [result page](./result.md) `head` element. -![LiveCodes styles](../../static/img/screenshots/css-processors.png) +![LiveCodes styles](../../static/img/screenshots/css-processors.jpg) ### Languages diff --git a/docs/docs/features/data-urls.md b/docs/docs/features/data-urls.md index 3614a0c18..e2bf69cbb 100644 --- a/docs/docs/features/data-urls.md +++ b/docs/docs/features/data-urls.md @@ -16,7 +16,7 @@ Data URLs can be created from: For local files on user's device. -[Assets screen](./assets.md) can be accessed from app menu → Assets. This works for any file type, including text files (e.g. stylesheets or scripts) and binary files like images. Generated data URLs are saved locally in the user's browser storage and are available across projects. +[Assets screen](./assets.md) can be accessed from Settings menu → Assets. This works for any file type, including text files (e.g. stylesheets or scripts) and binary files like images. Generated data URLs are saved locally in the user's browser storage and are available across projects. ### "Copy code as data URL" button diff --git a/docs/docs/features/default-template-language.md b/docs/docs/features/default-template-language.md index e00815fcc..bcc83ee80 100644 --- a/docs/docs/features/default-template-language.md +++ b/docs/docs/features/default-template-language.md @@ -6,9 +6,9 @@ The app can also be configured to load a default [user template](./templates.md# If you do not already have any user templates, save any loaded project as template: -App menu → Save as → Template. +Project menu → Save as → Template. -Then, in the user templates screen (app menu → New ... → My Templates), find your template and click "Set as default". +Then, in the user templates screen (Project menu → New ... → My Templates), find your template and click "Set as default". If you wish to clear that selection, find the default template in user templates and click "unset". diff --git a/docs/docs/features/deploy.md b/docs/docs/features/deploy.md index aebecd887..08f3dfd3d 100644 --- a/docs/docs/features/deploy.md +++ b/docs/docs/features/deploy.md @@ -2,13 +2,13 @@ The result page (of any number of projects) can be deployed and hosted at [GitHub Pages](https://pages.github.com/) (a free service from GitHub for hosting static websites). This requires login with a [GitHub account](./github-integration.md). -The `Deploy` screen can be accessed from the app menu → Deploy. +The `Deploy` screen can be accessed from the Project menu → Deploy. import RunInLiveCodes from '../../src/components/RunInLiveCodes.tsx'; -![LiveCodes Deploy](../../static/img/screenshots/deploy.jpg) +![LiveCodes Deploy](../../static/img/screenshots/deploy-1.jpg) The result page (and optionally the source code) is pushed to `gh-pages` branch of a **public** GitHub repo (new or existing). The page, shortly, becomes available on `https://{user}.github.io/{repo}/`. diff --git a/docs/docs/features/display-modes.md b/docs/docs/features/display-modes.md index 78cdcd767..7a89ad003 100644 --- a/docs/docs/features/display-modes.md +++ b/docs/docs/features/display-modes.md @@ -13,7 +13,7 @@ Example: https://livecodes.io/?template=react Screenshot: (App in full mode) -![full-mode](../../static/img/screenshots/focus-mode.png) +![full-mode](../../static/img/screenshots/mode-full.jpg) Demo: (Embedded playground in full mode) @@ -27,7 +27,7 @@ Example: https://livecodes.io/?template=react&mode=focus Screenshot: (focus mode) -![focus-mode](../../static/img/screenshots/focus-mode.png) +![focus-mode](../../static/img/screenshots/mode-focus.jpg) ## `simple` @@ -90,7 +90,7 @@ Demo: ## `result` -Shows the result page only, with a small overlay (appears on hover) that allows opening the project in the full playground. +Shows the result page only, with a drawer at the bottom (which can be closed) that allows opening the project in the full playground. Example: https://livecodes.io/?mode=result&template=react diff --git a/docs/docs/features/editor-settings.md b/docs/docs/features/editor-settings.md index 91c6e2a2c..858674cc1 100644 --- a/docs/docs/features/editor-settings.md +++ b/docs/docs/features/editor-settings.md @@ -2,30 +2,22 @@ LiveCodes allows a lot of flexibility for configuring which code editor to use and its settings. -`Editor Settings` screen can be accessed from app menu → Editor Settings. +`Editor Settings` screen can be accessed from Settings menu → Editor Settings. import RunInLiveCodes from '../../src/components/RunInLiveCodes.tsx'; -![LiveCodes Editor Settings](../../static/img/screenshots/editor-settings-1.png) +![LiveCodes Editor Settings](../../static/img/screenshots/editor-settings-1.jpg) -![LiveCodes Editor Settings](../../static/img/screenshots/editor-settings-2.png) +![LiveCodes Editor Settings](../../static/img/screenshots/editor-settings-2.jpg) -![LiveCodes Editor Settings](../../static/img/screenshots/editor-settings-3.png) - -![LiveCodes Editor Settings](../../static/img/screenshots/editor-settings-4.png) +![LiveCodes Editor Settings](../../static/img/screenshots/editor-settings-3.jpg) A preview code editor is displayed to preview the settings in real time. The settings selected in the `Editor Settings` screen are saved locally to [user settings](./user-settings.md) and are used subsequently. These include: -### App UI Language - -Explore and use LiveCodes in [your preferred language](./i18n.md). - -This is actually a global option and is **not specific to the editor**. Might be moved to a separate [User Settings](./user-settings.md) screen in the future. - ### Enable AI Code Assistant Enables the [AI code assistant](./ai.md). (Free and no account required) diff --git a/docs/docs/features/embeds.md b/docs/docs/features/embeds.md index 8fcb10f19..430ee8a80 100644 --- a/docs/docs/features/embeds.md +++ b/docs/docs/features/embeds.md @@ -16,7 +16,7 @@ The embedding web page can communicate with the playground using a powerful [SDK ### App Embed Screen -In the [standalone app](../getting-started.md#standalone-app), the Embed Screen can be accessed from app menu → Embed. +In the [standalone app](../getting-started.md#standalone-app), the Embed Screen can be accessed from Project menu → Embed. import RunInLiveCodes from '../../src/components/RunInLiveCodes.tsx'; @@ -24,9 +24,9 @@ import RunInLiveCodes from '../../src/components/RunInLiveCodes.tsx'; It shows a preview of the embedded playground, allows customizations of [embed options](../sdk/js-ts.md#embed-options) and provides generated code that can be added to the web page that will embed the playground. -![LiveCodes embed](../../static/img/screenshots/embed1.png) -![LiveCodes embed](../../static/img/screenshots/embed2.png) -![LiveCodes embed](../../static/img/screenshots/embed3.png) +![LiveCodes embed](../../static/img/screenshots/embed-1.jpg) +![LiveCodes embed](../../static/img/screenshots/embed-2.jpg) +![LiveCodes embed](../../static/img/screenshots/embed-3.jpg) :::info Note @@ -70,7 +70,7 @@ Some of the features of the full standalone app are not available or shown by de - All features that require authentication: e.g. [login/logout](./github-integration.md), [sync](./sync.md), [deploy](./deploy.md), [importing](./import.md) from private github repos. - [Broadcast](./broadcast.md). -- App menu. +- App menus. - Some tools in [tools pane](./tools-pane.md): e.g. open result page in new window, broadcast status. - [Welcome screen](./welcome.md). diff --git a/docs/docs/features/export.md b/docs/docs/features/export.md index 49ac644cc..40d03b50b 100644 --- a/docs/docs/features/export.md +++ b/docs/docs/features/export.md @@ -2,9 +2,9 @@ ## Exporting A Single Project -Project export can be accessed from app menu → Export. +Project export can be accessed from the Project menu → Export. -![LiveCodes Export](../../static/img/screenshots/export.png) +![LiveCodes Export](../../static/img/screenshots/export-1.jpg) Any project can be exported to: @@ -24,9 +24,9 @@ Any project can be exported to: ## Exporting Multiple Projects -Multiple projects can be exported in bulk from the [Saved Projects](./projects.md) screen (app menu → Open) using the button `Export All`. +Multiple projects can be exported in bulk from the [Saved Projects](./projects.md) screen (Project menu → Open) using the button `Export All`. -![saved projects](../../static/img/screenshots/saved-projects.png) +![saved projects](../../static/img/screenshots/export-2.jpg) This produces a JSON file containing an array of project configuration objects. They can be later imported in the same or a different device using the `Bulk Import` functionality in the [Import screen](./import.md#import-exported-livecodes-projects). diff --git a/docs/docs/features/external-resources.md b/docs/docs/features/external-resources.md index 66fd0efdd..d66e4c070 100644 --- a/docs/docs/features/external-resources.md +++ b/docs/docs/features/external-resources.md @@ -2,7 +2,7 @@ ## Stylesheets and Scripts -URLs to external CSS stylesheets and JS scripts can be added to the page from the UI using the app menu → External Resources. In addition, there is a button to the External Resources in the toolbar below the editors. +URLs to external CSS stylesheets and JS scripts can be added to the page from the UI using the Project menu → External Resources. In addition, there is a button to the External Resources in the toolbar below the editors. import RunInLiveCodes from '../../src/components/RunInLiveCodes.tsx'; @@ -12,7 +12,7 @@ URLs to stylesheets/scripts should be added each in a separate line. Stylesheets and scripts are loaded in the [result page](./result.md) before editor codes. Thus, CSS properties defined in external stylesheets can be overridden in the style editor. Global javascript variables defined in external scripts are available to code in the script editor. -![External Resources](/img/screenshots/external-resources.jpg) +![External Resources](/img/screenshots/resources.jpg) Importing and Exporting code to other services (e.g. Codepen and Github gists) takes into consiedration the external resources. @@ -35,7 +35,7 @@ jquery@3.6.3 ``` -![External Resources Search](/img/screenshots/external-resources-search.jpg) +![External Resources Search](/img/screenshots/resources-search.jpg) ## Fonts diff --git a/docs/docs/features/i18n.md b/docs/docs/features/i18n.md index 0f03980e6..9da727b7f 100644 --- a/docs/docs/features/i18n.md +++ b/docs/docs/features/i18n.md @@ -1,18 +1,20 @@ -# Internationalization (I18n) +# Internationalization (i18n) import LiveCodes from '../../src/components/LiveCodes.tsx'; LiveCodes currently supports UI internationalization. By default, the UI language will be automatically detected based on your browser settings. -You can easily switch between supported UI languages in the [Editor Settings](./editor-settings.md) screen, allowing you to explore and use LiveCodes in your preferred language! +You can easily switch between supported UI languages using the i18n menu, allowing you to explore and use LiveCodes in your preferred language! -If you are interested in contributing to the translation of LiveCodes, please refer to the [I18n Contribution Guide](https://github.com/live-codes/livecodes/blob/develop/docs/docs/contribution/i18n.md). +If you are interested in contributing to the translation of LiveCodes, please refer to the [i18n Contribution Guide](https://github.com/live-codes/livecodes/blob/develop/docs/docs/contribution/i18n.md). -![I18n in Editor Settings](../../static/img/screenshots/i18n.png) +![I18n menu](../../static/img/screenshots/i18n-1.jpg) -Demo: +![LiveCodes in Chinese](../../static/img/screenshots/i18n-2.jpg) - +Demo: ([Embedded playground](./embeds.md) with `appLanguage: 'zh-CN'`) + + ## Using SDK @@ -28,11 +30,11 @@ createPlayground('#container', { template: 'javascript', config: { appLanguage: Add the [query parameter](../configuration/query-params.md) `appLanguage` with your preferred language code. -https://livecodes.io?template=javascript&appLanguage=auto +https://livecodes.io?template=javascript&appLanguage=fr ## Related - [User Settings](./user-settings.md) - [Editor Settings](./editor-settings.md) - [Query params](../configuration/query-params.md) -- [I18n Contribution Guide](https://github.com/live-codes/livecodes/blob/develop/docs/docs/contribution/i18n.md) +- [i18n Contribution Guide](https://github.com/live-codes/livecodes/blob/develop/docs/docs/contribution/i18n.md) diff --git a/docs/docs/features/import.md b/docs/docs/features/import.md index ad2ebbd46..f15abb0a7 100644 --- a/docs/docs/features/import.md +++ b/docs/docs/features/import.md @@ -6,13 +6,15 @@ LiveCodes supports importing code from a wide variety of [sources](#sources). Th ### UI -The Import screen can be accessed from the app menu → Import. +The Import screen can be accessed from the Project menu → Import. import RunInLiveCodes from '../../src/components/RunInLiveCodes.tsx'; -![LiveCodes Import](../../static/img/screenshots/import.jpg) +![LiveCodes Import](../../static/img/screenshots/import-1.jpg) + +![LiveCodes Import](../../static/img/screenshots/import-2.jpg) ### Query Param diff --git a/docs/docs/features/index.md b/docs/docs/features/index.md index 22882558f..949f61ed8 100644 --- a/docs/docs/features/index.md +++ b/docs/docs/features/index.md @@ -8,7 +8,11 @@ sidebar_class_name: exclude_from_sidebar _LiveCodes_ is an open-source, client-side, code playground that runs in the browser. It enables quick prototyping and experimenting with a wide variety of [technologies](../languages/index.md) (including 80+ languages, frameworks and processors) without having to manually setup a development environment for each. The [result](./result.md) is displayed as a regular web page. The rapid feedback of previewing the result page without waiting for build steps significantly boosts productivity. -![LiveCodes screenshot](../../static/img/screenshots/features.jpg) +![LiveCodes screenshot](../../static/img/screenshots/features-1.jpg) + +![LiveCodes screenshot](../../static/img/screenshots/features-2.jpg) + +![LiveCodes screenshot](../../static/img/screenshots/features-3.jpg) In this page, a quick overview of the important features are presented. A more detailed description of each feature is described in the following sections. @@ -48,7 +52,7 @@ Projects can be [deployed](./deploy.md) to public URLs that can be shared with o A large number of [starter templates](./templates.md) are available. They can be used to get you started with most of the supported technologies. -![Starter templates](../../static/img/screenshots/templates1.png) +![Starter templates](../../static/img/screenshots/templates-1.jpg) ## Assets diff --git a/docs/docs/features/intellisense.md b/docs/docs/features/intellisense.md index 84d8a2d1e..01d72e9e9 100644 --- a/docs/docs/features/intellisense.md +++ b/docs/docs/features/intellisense.md @@ -8,7 +8,7 @@ This not only works when the editor language is TypeScript, but also works with Example: -![LiveCodes Autocomplete](../../static/img/screenshots/autocomplete.png) +![LiveCodes Autocomplete](../../static/img/screenshots/autocomplete.jpg) ## Types for imported npm packages @@ -16,9 +16,9 @@ LiveCodes will try to automatically find type definitions for npm modules import These are examples for automatically loading React types with autocomplete and hover info: -![LiveCodes Intellisense](../../static/img/screenshots/intellisense1.jpg) +![LiveCodes Intellisense](../../static/img/screenshots/intellisense-1.jpg) -![LiveCodes Intellisense](../../static/img/screenshots/intellisense2.jpg) +![LiveCodes Intellisense](../../static/img/screenshots/intellisense-2.jpg) ## TypeScript TwoSlash @@ -26,9 +26,9 @@ The code editor supports [TypeScript TwoSlash](https://github.com/microsoft/Type This is supported in [JavaScript](../languages/javascript.md), [TypeScript](../languages/typescript.md), [JSX](../languages/jsx.md) and [TSX](../languages/tsx.md). This also includes [Babel](../languages/babel.md), [Sucrase](../languages/sucrase.md), [Solid](../languages/solid.md), [React Native](../languages/react-native.md), etc. -![TypeScript TwoSlash](../../static/img/screenshots/twoslash.png) +![TypeScript TwoSlash](../../static/img/screenshots/twoslash.jpg) -![TwoSlash in JSX](../../static/img/screenshots/twoslash-jsx.png) +![TwoSlash in JSX](../../static/img/screenshots/twoslash-jsx.jpg) ## Custom Types diff --git a/docs/docs/features/mobile.md b/docs/docs/features/mobile.md index e0f750f50..e7cda69e5 100644 --- a/docs/docs/features/mobile.md +++ b/docs/docs/features/mobile.md @@ -2,8 +2,8 @@ LiveCodes provides a responsive layout that adapts to different screen sizes. Don't wait to be on your desk. Try your ideas on the go! -Projects that you created on mobile can be easily [shared](./share.md) or [synched](./sync.md) across devices. You can even share using QR code. +Projects that you create on mobile can be easily [shared](./share.md) or [synchronized](./sync.md) across devices. You can even share using QR code. -![Responsive layout](../../static/img/screenshots/slider/responsive.jpg) +![Responsive layout](../../static/img/screenshots/responsive.jpg) By default, LiveCodes uses the touch-friendly [CodeMirror 6](https://codemirror.net/) editor on mobile. This is configurable in the [editor settings](./editor-settings.md) and can be changed at any time. diff --git a/docs/docs/features/permanent-url.md b/docs/docs/features/permanent-url.md index cce5856a8..10b4ec6d5 100644 --- a/docs/docs/features/permanent-url.md +++ b/docs/docs/features/permanent-url.md @@ -49,7 +49,7 @@ ${' '}}); ## Get Permanent URL -You can get the permanent URL for the app from the [About screen](pathname:///../?screen=about) (App menu → About). By default, the code generated in the [Embed screen](./embeds.md#app-embed-screen) uses permanent URL. +You can get the permanent URL for the app from the [About screen](pathname:///../?screen=about) (Help menu → About). By default, the code generated in the [Embed screen](./embeds.md#app-embed-screen) uses permanent URL. Alternatively, open the browser console of the standalone app (e.g. https://livecodes.io), and run this: export const GetPermanentUrl = () => { diff --git a/docs/docs/features/projects.md b/docs/docs/features/projects.md index b609a157b..aa600dea9 100644 --- a/docs/docs/features/projects.md +++ b/docs/docs/features/projects.md @@ -34,15 +34,15 @@ For the full list of supported languages, check the [Languages section](../langu ## Organizing Projects -Projects can be saved to the local device browser storage from app menu → Save / app menu → Save as → Fork (New Project) or using the keyboard shortcut Ctrl + S. +Projects can be saved to the local device browser storage from Project menu → Save / Project menu → Save as → Fork (New Project) or using the keyboard shortcut Ctrl + S. -The list of saved projects can be accessed from app menu → Open. +The list of saved projects can be accessed from Project menu → Open. -![LiveCodes Projects](../../static/img/screenshots/saved-projects.png). +![LiveCodes Projects](../../static/img/screenshots/saved-projects-1.jpg). Saved projects can be sorted by title or date (asc/desc). They can also be filtered by language and/or tags. You may, as well, search for a project by title or description. -Project title, description and tags can be edited from app menu → Project Info. +Project title, description and tags can be edited from Project menu → Project Info. Projects can be [imported](./import.md), [exported](./export.md), [synchronized](./sync.md), [backed up and restored](./backup-restore.md). diff --git a/docs/docs/features/result.md b/docs/docs/features/result.md index 1f666d173..b7a9459ff 100644 --- a/docs/docs/features/result.md +++ b/docs/docs/features/result.md @@ -64,13 +64,13 @@ This is the pseudo-code for the structure of the result page (inspired by [CodeP The zoom button in the [tools pane](./tools-pane.md) below result page, allows you to toggle result page zoom (1x/0.5x/0.25x). -![Result page zoom](../../static/img/screenshots/result-zoom.jpg) +![Result page zoom](../../static/img/screenshots/zoom.jpg) ## Open in new window From the [tools pane](./tools-pane.md), the result page can be viewed in a separate window. -![Open in new window](../../static/img/screenshots/result-new-window.jpg) +![Open in new window](../../static/img/screenshots/new-window.jpg) :::caution @@ -84,9 +84,9 @@ If you need to share a project, use the [Share screen](./share.md). While, if yo The spacing between elements on the result page can be measured by adding [Spacing.js](https://spacingjs.com/) to the result page. -![Show Spacings](../../static/img/screenshots/spacings.jpg) +![Show Spacings](../../static/img/screenshots/spacing.jpg) -1. Enable `Show Spacing` setting in the app menu. +1. Enable `Show Spacing` setting in the Settings menu. 2. Move your cursor to an element and press Alt on Windows, or Option on a Mac. 3. Move your cursor to another element, the measurement results will be there. diff --git a/docs/docs/features/share.md b/docs/docs/features/share.md index b45ce0557..83b235eff 100644 --- a/docs/docs/features/share.md +++ b/docs/docs/features/share.md @@ -4,15 +4,15 @@ It is easy to share LiveCodes projects! A URL is generated to load the shared project. This URL can be copied or shared to different social media. -The share screen can be accessed from the share icon at the top right or from the app menu → Share. +The share screen can be accessed from the share icon at the top right or from the Project menu → Share. -![LiveCodes Share](../../static/img/screenshots/share.jpg) +![LiveCodes Share](../../static/img/screenshots/share-1.jpg) By default, the generated URL encodes the project configuration in a base-64-encoded compressed query string. This step is generated locally in the browser without sending the code to any server. However, depending on the size of the project, the URL can be very long. The length of the URL is indicated in the share screen. [Try not to use very long URLs](https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers) to ensure cross-browser compatibility. When requested by the user, short URLs can be generated. This requires sending the project configuration (**including source code**) to a server that saves the code and provides a short Id which can be used to retrieve the project. -![LiveCodes Share - short URL](../../static/img/screenshots/share2.jpg) +![LiveCodes Share - short URL](../../static/img/screenshots/share-2.jpg) :::caution @@ -32,7 +32,7 @@ However, [**self-hosted apps**](./self-hosting.md) use the free service [dpaste] QR code can be generated for the share URL. This can then be scanned by any QR code scanner (e.g. mobile/tablet camera) to load the project on other devices without having to send the link. Please note that generating QR code also requires generating a short URL (code is sent to the share service - see above). -![LiveCodes Share - QR code](../../static/img/screenshots/share-qrcode.jpg) +![LiveCodes Share - QR code](../../static/img/screenshots/share-3.jpg) ## Related diff --git a/docs/docs/features/snippets.md b/docs/docs/features/snippets.md index caedabe02..78d9b5fdb 100644 --- a/docs/docs/features/snippets.md +++ b/docs/docs/features/snippets.md @@ -4,7 +4,9 @@ LiveCodes supports saving and organizing code snippets in different languages. Code snippets are saved locally on user's device. However, they are supported in [sync](./sync.md), [backup](./backup-restore.md#backup) and [restore](./backup-restore.md#restore). -Code snippets screen can be accessed from app menu → Code Snippets. +Code snippets screen can be accessed from Settings menu → Code Snippets. + +![Code Snippets](../../static/img/screenshots/snippets-1.jpg) import RunInLiveCodes from '../../src/components/RunInLiveCodes.tsx'; @@ -12,13 +14,13 @@ import RunInLiveCodes from '../../src/components/RunInLiveCodes.tsx'; Each snippet has a title, description, language and code. -![Code Snippets](../../static/img/screenshots/add-snippet.png) +![Code Snippets](../../static/img/screenshots/snippets-add.jpg) After adding snippets they can be sorted (by date modified or title), filtered (by language) or searched. -![Code Snippets](../../static/img/screenshots/snippets.jpg) +![Code Snippets](../../static/img/screenshots/snippets-list.jpg) -Clicking on a snippet causes the code to be copied to clipboard. This can then be pasted in projects. +Code snippets can then be copied to clipboard and pasted in projects. ## Related diff --git a/docs/docs/features/sync.md b/docs/docs/features/sync.md index 96e484e45..d48740838 100644 --- a/docs/docs/features/sync.md +++ b/docs/docs/features/sync.md @@ -4,13 +4,13 @@ LiveCodes local data can be synchronized to a GitHub repo. This can be used as a A GitHub account is required. The user must give access to [(Private Repos) while logging in](./github-integration.md). -The Sync screen can be accessed from the app menu → Sync. +The Sync screen can be accessed from the Settings menu → Sync. import RunInLiveCodes from '../../src/components/RunInLiveCodes.tsx'; -![LiveCodes Sync](../../static/img/screenshots/sync.png) +![LiveCodes Sync](../../static/img/screenshots/sync.jpg) Data can be synchronized to a new (**private**) or existing repo. @@ -18,7 +18,7 @@ The data is synchronized with the `main` branch in a directory named `livecodes- If `Auto sync` is selected, the sync will be attempted every 5 minutes. Remote files are downloaded only when changed (e.g. sync from another device). -`Auto sync` can be turned off and on by the switch on the app menu. +`Auto sync` can be turned off and on by the switch on the Settings menu. The sync can be manually triggered at any time from the Sync UI. Information regarding the last sync time and repo are displayed on the UI screen. diff --git a/docs/docs/features/templates.md b/docs/docs/features/templates.md index 842f9eb66..a2ff37d9b 100644 --- a/docs/docs/features/templates.md +++ b/docs/docs/features/templates.md @@ -6,7 +6,7 @@ A new project can be based on any of the provided [starter templates](#starter-t A new project (from template) can be started from: -- App menu → New +- Project menu → New - [Welcome Screen](./welcome.md) → New - Direct URL: https://livecodes.io?new @@ -30,9 +30,9 @@ The following list of starter templates are available: Any project loaded in LiveCodes can be saved as a user template, to be used later as a starting point for new projects. -A project can be saved as a user template from app menu → Save as → Template. +A project can be saved as a user template from Project menu → Save as → Template. -It can then be accessed from app menu → New ... → My Templates. +It can then be accessed from Project menu → New ... → My Templates. A user template can be set as [default template](./default-template-language.md) to be automatically loaded when loading the app. diff --git a/docs/docs/features/tests.md b/docs/docs/features/tests.md index d317fc7fd..65f819eae 100644 --- a/docs/docs/features/tests.md +++ b/docs/docs/features/tests.md @@ -10,9 +10,9 @@ The automated tests are run by the Screenshots: -![Livecodes Tests](/img/screenshots/tests.png) +![Livecodes Tests](/img/screenshots/tests.jpg) -![Livecodes Tests](/img/screenshots/test-editor.png) +![Livecodes Tests](/img/screenshots/test-editor.jpg) ## Use Cases diff --git a/docs/docs/features/themes.md b/docs/docs/features/themes.md index 970af388c..559c3f8b1 100644 --- a/docs/docs/features/themes.md +++ b/docs/docs/features/themes.md @@ -1,22 +1,47 @@ # Themes -LiveCodes comes with dark and light themes. +LiveCodes comes with dark and light themes. In addition, a theme color can be set to change the app color. -Themes can be set in: +## Theme -- UI: app menu → Dark theme switch. +Dark/Light theme can be set in: + +- UI, either: + + - Dark/Light theme switch in toolbar + - Settings menu → Dark theme switch - [Query params](../configuration/query-params.md): `?theme=dark` or `?theme=light`. e.g. https://livecodes.io/?theme=light - [Configuration object](../configuration/configuration-object.md): [`theme`](../configuration/configuration-object.md#theme) property. -![LiveCodes dark theme](../../static/img/screenshots/dark-theme.jpg) +![LiveCodes dark theme](../../static/img/screenshots/themes-1.jpg)

LiveCodes dark theme
-![LiveCodes light theme](../../static/img/screenshots/light-theme-.jpg) +![LiveCodes light theme](../../static/img/screenshots/themes-2.jpg)
LiveCodes light theme
+## Theme Color + +Similarly, a theme color can be set in: + +- UI: Settings menu → Color + +- [Query params](../configuration/query-params.md): `?themeColor={color}`. + e.g. https://livecodes.io/?themeColor=lightblue + +- [Configuration object](../configuration/configuration-object.md): [`themeColor`](../configuration/configuration-object.md#themecolor) property. + +![LiveCodes alternate theme color](../../static/img/screenshots/themes-3.jpg) + +
Change theme color from UI
+ +![LiveCodes custom theme color](../../static/img/screenshots/themes-4.jpg) + +
Custom theme color
+ +:::info Note Please note that editor themes can be set in the [editor settings](./editor-settings.md) or using the [`editorTheme`](../configuration/configuration-object.md#editortheme) configuration option. diff --git a/docs/docs/features/user-management.md b/docs/docs/features/user-management.md index 77be938be..edf643c86 100644 --- a/docs/docs/features/user-management.md +++ b/docs/docs/features/user-management.md @@ -28,7 +28,7 @@ If the user logs off, the data is cleared from the app, but is kept in the brows ## Login/Logout -Login/Logout links are accessible from the app menu. +Login/Logout links are accessible at the top right corner of the app. :::info note diff --git a/docs/docs/features/user-settings.md b/docs/docs/features/user-settings.md index c065cfaa5..3224af85a 100644 --- a/docs/docs/features/user-settings.md +++ b/docs/docs/features/user-settings.md @@ -2,17 +2,20 @@ A user can select various settings that are stored locally in the browser and are subsequently used. -User settings can be configured in app menu. These include settings like: +User settings can be configured in Settings menu. These include settings like: - Auto-update - Auto-save - Delay (before auto-update and auto-save) - [Format on-save](./code-format.md#format-on-save) - [Theme](./themes.md) (light/dark) +- [Theme color](./themes.md#theme-color) - [Recover unsaved projects](./recover.md) - [Show spacing](./result.md#show-spacings) +- Layout (responsive/vertical/horizontal) - [Sync](./sync.md) - Show [welcome screen](./welcome.md) +- [App language](./i18n.md) The settings selected in the [`Editor Settings`](./editor-settings.md) screen are also saved locally as user settings. diff --git a/docs/docs/languages/gleam.md b/docs/docs/languages/gleam.md index 12ad3add8..a522aa11e 100644 --- a/docs/docs/languages/gleam.md +++ b/docs/docs/languages/gleam.md @@ -54,7 +54,7 @@ Note that the built code was committed to the repo by clearing out `.gitignore` The built code can then by accessed from a [CDN that mirrors GitHub](https://www.jsdelivr.com/?docs=gh), like this: `https://cdn.jsdelivr.net/gh/live-codes/gleam-precompiled@main/...` -Built modules can then be declared in [custom settings](../advanced/custom-settings.md) (App menu → Custom Settings), under the `gleam` property, by adding a `modules` property. +Built modules can then be declared in [custom settings](../advanced/custom-settings.md) (Project menu → Custom Settings), under the `gleam` property, by adding a `modules` property. The `modules` property is an object that has the module name as the key. The value is an object with the following properties: @@ -80,7 +80,7 @@ Example: See the [demo below](#example-usage) ([open in LiveCodes](https://livecodes.io/?template=gleam)). If `compiledUrl` property is not specified, the JavaScript module is imported from this URL pattern: `{module_name}.mjs` (example: `plinth/browser/document.mjs`). -This can then be [mapped (using import maps)](../features/module-resolution.md#custom-module-resolution) in [custom settings](../advanced/custom-settings.md) (App menu → Custom Settings) to the full URL of the compiled JavaScript code. +This can then be [mapped (using import maps)](../features/module-resolution.md#custom-module-resolution) in [custom settings](../advanced/custom-settings.md) (Project menu → Custom Settings) to the full URL of the compiled JavaScript code. Example: @@ -107,7 +107,7 @@ Example: ### Externals -[External functions](https://tour.gleam.run/advanced-features/externals/) written in JavaScript can also be used. An external function has the `@external` attribute on it. It needs to specify a "relative" URL specifying the location of the external code. This URL is [mapped (using import maps)](../features/module-resolution.md#custom-module-resolution) in [custom settings](../advanced/custom-settings.md) (App menu → Custom Settings) to the full URL of the script that contains the code. +[External functions](https://tour.gleam.run/advanced-features/externals/) written in JavaScript can also be used. An external function has the `@external` attribute on it. It needs to specify a "relative" URL specifying the location of the external code. This URL is [mapped (using import maps)](../features/module-resolution.md#custom-module-resolution) in [custom settings](../advanced/custom-settings.md) (Project menu → Custom Settings) to the full URL of the script that contains the code. **Example:** diff --git a/docs/docs/languages/jsx.md b/docs/docs/languages/jsx.md index a80a4a104..5f7c26489 100644 --- a/docs/docs/languages/jsx.md +++ b/docs/docs/languages/jsx.md @@ -67,7 +67,7 @@ export const rootDemo = { html: `
Loading...
`, jsx: To disable auto-rendering, set the [custom settings](#custom-settings) `disableAutoRender` property to `true`. -export const disableAutoRenderDemo = {markup: {language: "html", content: `JSX auto-rendering is disabled. Set from app menu → Custom Settings.`}, script: {language: "jsx", content: `export default function App() {\n return

Hello World!

;\n}`}, customSettings: {"jsx": {"disableAutoRender": true}}} +export const disableAutoRenderDemo = {markup: {language: "html", content: `JSX auto-rendering is disabled. Set from Project menu → Custom Settings.`}, script: {language: "jsx", content: `export default function App() {\n return

Hello World!

;\n}`}, customSettings: {"jsx": {"disableAutoRender": true}}} diff --git a/docs/src/components/HomepageCarousel.tsx b/docs/src/components/HomepageCarousel.tsx index e73c8c79b..9338930a4 100644 --- a/docs/src/components/HomepageCarousel.tsx +++ b/docs/src/components/HomepageCarousel.tsx @@ -5,107 +5,119 @@ import styles from './HomepageCarousel.module.css'; const screenshots = [ { label: 'A large collection of languages', - url: './img/screenshots/slider/languages.jpg', + url: './img/screenshots/languages-3.jpg', }, { label: 'Lots of features', - url: './img/screenshots/slider/export.jpg', + url: './img/screenshots/export-1.jpg', + }, + { + label: 'Even more features', + url: './img/screenshots/snippets-1.jpg', }, { label: 'Starter templates for new projects', - url: './img/screenshots/slider/templates1.jpg', + url: './img/screenshots/templates-1.jpg', }, { label: '... many starter templates', - url: './img/screenshots/slider/templates2.jpg', + url: './img/screenshots/templates-2.jpg', }, { label: 'Welcome screen - for a quick start', - url: './img/screenshots/slider/welcome.jpg', + url: './img/screenshots/welcome-1.jpg', }, { label: 'Keep your projects organized', - url: './img/screenshots/slider/saved-projects.jpg', + url: './img/screenshots/saved-projects-1.jpg', }, { label: 'Add external resources', - url: './img/screenshots/slider/external-resources.png', + url: './img/screenshots/resources.jpg', }, { label: 'CSS Processors', - url: './img/screenshots/slider/css-processors.jpg', + url: './img/screenshots/css-processors.jpg', + }, + { + label: 'IntelliSense (autocomplete)', + url: './img/screenshots/intellisense-3.jpg', }, { - label: 'IntelliSense (autocomplete) - format code', - url: './img/screenshots/slider/intellisense.jpg', + label: 'AI Code Assistant', + url: './img/screenshots/ai-1.jpg', }, { label: 'Console for quick inspection', - url: './img/screenshots/slider/console.jpg', + url: './img/screenshots/console-1.jpg', }, { label: 'View compiled code', - url: './img/screenshots/slider/compiled-code.jpg', + url: './img/screenshots/compiled-code-1.jpg', }, { label: 'Import code from many sources', - url: './img/screenshots/slider/import.png', + url: './img/screenshots/import-2.jpg', }, { label: 'Embed projects into your web pages', - url: './img/screenshots/slider/embed.jpg', + url: './img/screenshots/embed-1.jpg', }, { label: 'Share your projects with others', - url: './img/screenshots/slider/share.jpg', + url: './img/screenshots/share-1.jpg', }, { label: 'Deploy to GitHub Pages', - url: './img/screenshots/slider/deploy.jpg', + url: './img/screenshots/deploy-1.jpg', }, { label: 'Broadcast real-time code changes', - url: './img/screenshots/slider/broadcast.png', + url: './img/screenshots/broadcast-1.jpg', }, { label: 'Project info', - url: './img/screenshots/slider/project-info.png', + url: './img/screenshots/project-info.jpg', }, { label: 'Assets manager', - url: './img/screenshots/slider/assets.jpg', + url: './img/screenshots/assets-2.jpg', }, { label: 'Code snippets', - url: './img/screenshots/slider/snippets.jpg', + url: './img/screenshots/snippets-list.jpg', }, { label: 'Run automated tests!', - url: './img/screenshots/slider/tests.jpg', + url: './img/screenshots/tests.jpg', }, { label: 'Sync with your other devices', - url: './img/screenshots/slider/sync.png', + url: './img/screenshots/sync.jpg', }, { label: 'Backup and restore your data', - url: './img/screenshots/slider/backup-restore.png', + url: './img/screenshots/backup-restore-1.jpg', }, { label: 'Fine-tune your editor settings', - url: './img/screenshots/slider/editor-settings.png', + url: './img/screenshots/editor-settings-1.jpg', }, { label: 'Emmet support and Vim/Emacs modes', - url: './img/screenshots/slider/editor-settings2.png', + url: './img/screenshots/editor-settings-3.jpg', + }, + { + label: 'Select your favorite theme/color', + url: './img/screenshots/themes-5.jpg', }, { - label: 'Light theme', - url: './img/screenshots/slider/light-theme.jpg', + label: 'Change UI language to your preferred one', + url: './img/screenshots/i18n-1.jpg', }, { label: 'Responsive layout', - url: './img/screenshots/slider/responsive.jpg', + url: './img/screenshots/responsive.jpg', }, ]; diff --git a/docs/src/components/LiveCodes.tsx b/docs/src/components/LiveCodes.tsx index 361d3852c..07e7fa800 100644 --- a/docs/src/components/LiveCodes.tsx +++ b/docs/src/components/LiveCodes.tsx @@ -82,6 +82,10 @@ onMount(() => { }} appUrl={appUrl} {...props} + config={{ + themeColor: 'hsl(166, 40%, 50%', + ...(typeof props.config === 'object' ? props.config : {}), + }} > {props.showCode !== false && ( { await app.waitForFunction(`document.activeElement.closest("${selector}") != null`); }; -export const runButtonSelector = '[alt="Run"]'; +export const runButtonSelector = '#run-button'; diff --git a/e2e/specs/compilers.spec.ts b/e2e/specs/compilers.spec.ts index e6b5d5eaf..52edaf681 100644 --- a/e2e/specs/compilers.spec.ts +++ b/e2e/specs/compilers.spec.ts @@ -8,18 +8,18 @@ test.describe('Compiler Results', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 1)'); - await app.click('text=HTML'); + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text="HTML"'); await waitForEditorFocus(app); await page.keyboard.type('hello, '); - await app.click(':nth-match([data-hint="Change Language"], 2)'); - await app.click('text=CSS'); + await app.click(':nth-match([title="Change Language"], 2)'); + await app.click('text="CSS"'); await waitForEditorFocus(app); await page.keyboard.type('body {color: blue;}'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); - await app.click('text=JavaScript'); + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text="JavaScript"'); await waitForEditorFocus(app); await page.keyboard.type('document.body.innerHTML += "world!"'); @@ -37,7 +37,7 @@ test.describe('Compiler Results', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 1)'); + await app.click(':nth-match([title="Change Language"], 1)'); await app.click('text=Markdown'); await waitForEditorFocus(app); await app.page().keyboard.type('# Hi There'); @@ -53,7 +53,7 @@ test.describe('Compiler Results', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 1)'); + await app.click(':nth-match([title="Change Language"], 1)'); await app.click('text=MDX'); await waitForEditorFocus(app); await app.page().keyboard.type(` @@ -62,7 +62,7 @@ import {Hello} from './script'; `); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=JSX'); await waitForEditorFocus(app); await app.page().keyboard.type(` @@ -81,7 +81,7 @@ export const Hello = (props) =>

Hello, {props.title}!

; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 1)'); + await app.click(':nth-match([title="Change Language"], 1)'); await app.click('text=Astro'); await waitForEditorFocus(app); await app.page().keyboard.type(`--- @@ -101,7 +101,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 1)'); + await app.click(':nth-match([title="Change Language"], 1)'); await app.click('text=Pug'); await waitForEditorFocus(app); await page.keyboard.type('h1 Hi There'); @@ -117,7 +117,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -125,8 +125,8 @@ const title = "World"; await page.keyboard.type(`{"template":{"data":{"name": "Haml"}}}`); await app.click('button:has-text("Load"):visible'); - await app.click(':nth-match([data-hint="Change Language"], 1)'); - await app.click('text=Haml'); + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text="Haml"'); await waitForEditorFocus(app); await page.keyboard.type('.content Hello, #{name}!'); @@ -141,7 +141,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 1)'); + await app.click(':nth-match([title="Change Language"], 1)'); await app.click('text=AsciiDoc'); await waitForEditorFocus(app); await page.keyboard.type('== Hello, World!'); @@ -157,7 +157,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -165,7 +165,7 @@ const title = "World"; await page.keyboard.type(`{"template":{"data":{"name": "Mustache"}}}`); await app.click('button:has-text("Load"):visible'); - await app.click(':nth-match([data-hint="Change Language"], 1)'); + await app.click(':nth-match([title="Change Language"], 1)'); await app.click('text=Mustache'); await waitForEditorFocus(app); await page.keyboard.type(`

Welcome to {{name}}

`); @@ -181,7 +181,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -189,12 +189,12 @@ const title = "World"; await page.keyboard.type(`{"template":{"prerender": false}}`); await app.click('button:has-text("Load"):visible'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=JavaScript'); await waitForEditorFocus(app); await page.keyboard.type(`window.livecodes.templateData = { name: 'Mustache' };`); - await app.click(':nth-match([data-hint="Change Language"], 1)'); + await app.click(':nth-match([title="Change Language"], 1)'); await app.click('text=Mustache'); await waitForEditorFocus(app); await page.keyboard.type(`

Welcome to {{name}}

`); @@ -211,7 +211,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -219,7 +219,7 @@ const title = "World"; await page.keyboard.type(`{"template":{"data":{"name": "Handlebars"}}}`); await app.click('button:has-text("Load"):visible'); - await app.click(':nth-match([data-hint="Change Language"], 1)'); + await app.click(':nth-match([title="Change Language"], 1)'); await app.click('text=Handlebars'); await waitForEditorFocus(app); await page.keyboard.type(`

Welcome to {{name}}

`); @@ -235,7 +235,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -243,12 +243,12 @@ const title = "World"; await page.keyboard.type(`{"template":{"prerender": false}}`); await app.click('button:has-text("Load"):visible'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=JavaScript'); await waitForEditorFocus(app); await page.keyboard.type(`window.livecodes.templateData = { name: 'Handlebars' };`); - await app.click(':nth-match([data-hint="Change Language"], 1)'); + await app.click(':nth-match([title="Change Language"], 1)'); await app.click('text=Handlebars'); await waitForEditorFocus(app); await page.keyboard.type(`

Welcome to {{name}}

`); @@ -265,7 +265,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -273,7 +273,7 @@ const title = "World"; await page.keyboard.type(`{"template":{"data":{"name": "Nunjucks"}}}`); await app.click('button:has-text("Load"):visible'); - await app.click(':nth-match([data-hint="Change Language"], 1)'); + await app.click(':nth-match([title="Change Language"], 1)'); await app.click('text=Nunjucks'); await waitForEditorFocus(app); await page.keyboard.type(`

Welcome to {{name}}

`); @@ -289,7 +289,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -297,12 +297,12 @@ const title = "World"; await page.keyboard.type(`{"template":{"prerender": false}}`); await app.click('button:has-text("Load"):visible'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=JavaScript'); await waitForEditorFocus(app); await page.keyboard.type(`window.livecodes.templateData = { name: 'Nunjucks' };`); - await app.click(':nth-match([data-hint="Change Language"], 1)'); + await app.click(':nth-match([title="Change Language"], 1)'); await app.click('text=Nunjucks'); await waitForEditorFocus(app); await page.keyboard.type(`

Welcome to {{name}}

`); @@ -319,7 +319,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -327,7 +327,7 @@ const title = "World"; await page.keyboard.type(`{"template":{"data":{"name": "EJS"}}}`); await app.click('button:has-text("Load"):visible'); - await app.click(':nth-match([data-hint="Change Language"], 1)'); + await app.click(':nth-match([title="Change Language"], 1)'); await app.click('text=EJS'); await waitForEditorFocus(app); await page.keyboard.type(`

Welcome to <%= name %>

`); @@ -343,7 +343,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -351,12 +351,12 @@ const title = "World"; await page.keyboard.type(`{"template":{"prerender": false}}`); await app.click('button:has-text("Load"):visible'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=JavaScript'); await waitForEditorFocus(app); await page.keyboard.type(`window.livecodes.templateData = { name: 'EJS' };`); - await app.click(':nth-match([data-hint="Change Language"], 1)'); + await app.click(':nth-match([title="Change Language"], 1)'); await app.click('text=EJS'); await waitForEditorFocus(app); await page.keyboard.type(`

Welcome to <%= name %>

`); @@ -373,7 +373,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -381,8 +381,8 @@ const title = "World"; await page.keyboard.type(`{"template":{"data":{"name": "Eta"}}}`); await app.click('button:has-text("Load"):visible'); - await app.click(':nth-match([data-hint="Change Language"], 1)'); - await app.click('text=Eta'); + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text="Eta"'); await waitForEditorFocus(app); await page.keyboard.type(`

Welcome to <%= it.name %>

`); @@ -397,7 +397,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -405,13 +405,13 @@ const title = "World"; await page.keyboard.type(`{"template":{"prerender": false}}`); await app.click('button:has-text("Load"):visible'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=JavaScript'); await waitForEditorFocus(app); await page.keyboard.type(`window.livecodes.templateData = { name: 'Eta' };`); - await app.click(':nth-match([data-hint="Change Language"], 1)'); - await app.click('text=Eta'); + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text="Eta"'); await waitForEditorFocus(app); await page.keyboard.type(`

Welcome to <%= it.name %>

`); @@ -427,7 +427,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -435,7 +435,7 @@ const title = "World"; await page.keyboard.type(`{"template":{"data":{"name":"liquid"}}}`); await app.click('button:has-text("Load"):visible'); - await app.click(':nth-match([data-hint="Change Language"], 1)'); + await app.click(':nth-match([title="Change Language"], 1)'); await app.click('text=Liquid'); await waitForEditorFocus(app); await page.keyboard.type(`{{ name | capitalize | prepend: "Welcome to "}}`); @@ -451,7 +451,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -459,12 +459,12 @@ const title = "World"; await page.keyboard.type(`{"template":{"prerender": false}}`); await app.click('button:has-text("Load"):visible'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=JavaScript'); await waitForEditorFocus(app); await page.keyboard.type(`window.livecodes.templateData = { name: 'liquid' };`); - await app.click(':nth-match([data-hint="Change Language"], 1)'); + await app.click(':nth-match([title="Change Language"], 1)'); await app.click('text=Liquid'); await waitForEditorFocus(app); await page.keyboard.type(`{{ name | capitalize | prepend: "Welcome to "}}`); @@ -481,7 +481,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -489,7 +489,7 @@ const title = "World"; await page.keyboard.type(`{"template":{"data":{"name":"doT"}}}`); await app.click('button:has-text("Load"):visible'); - await app.click(':nth-match([data-hint="Change Language"], 1)'); + await app.click(':nth-match([title="Change Language"], 1)'); await app.click('text=doT'); await waitForEditorFocus(app); await page.keyboard.type(`

Welcome to {{=it.name}}

`); @@ -505,7 +505,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -513,12 +513,12 @@ const title = "World"; await page.keyboard.type(`{"template":{"prerender": false}}`); await app.click('button:has-text("Load"):visible'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=JavaScript'); await waitForEditorFocus(app); await page.keyboard.type(`window.livecodes.templateData = { name: 'doT' };`); - await app.click(':nth-match([data-hint="Change Language"], 1)'); + await app.click(':nth-match([title="Change Language"], 1)'); await app.click('text=doT'); await waitForEditorFocus(app); await page.keyboard.type(`

Welcome to {{=it.name}}

`); @@ -535,7 +535,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -543,7 +543,7 @@ const title = "World"; await page.keyboard.type(`{"template":{"data":{"name": "Twig"}}}`); await app.click('button:has-text("Load"):visible'); - await app.click(':nth-match([data-hint="Change Language"], 1)'); + await app.click(':nth-match([title="Change Language"], 1)'); await app.click('text=Twig'); await waitForEditorFocus(app); await page.keyboard.type(`

Welcome to {{ name }}

`); @@ -559,7 +559,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -567,12 +567,12 @@ const title = "World"; await page.keyboard.type(`{"template":{"prerender": false}}`); await app.click('button:has-text("Load"):visible'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=JavaScript'); await waitForEditorFocus(app); await page.keyboard.type(`window.livecodes.templateData = { name: 'Twig' };`); - await app.click(':nth-match([data-hint="Change Language"], 1)'); + await app.click(':nth-match([title="Change Language"], 1)'); await app.click('text=Twig'); await waitForEditorFocus(app); await page.keyboard.type(`

Welcome to {{ name }}

`); @@ -589,7 +589,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -597,7 +597,7 @@ const title = "World"; await page.keyboard.type(`{"template":{"data":{"name": "Vento"}}}`); await app.click('button:has-text("Load"):visible'); - await app.click(':nth-match([data-hint="Change Language"], 1)'); + await app.click(':nth-match([title="Change Language"], 1)'); await app.click('text=Vento'); await waitForEditorFocus(app); await page.keyboard.type(`

Welcome to {{ name }}

`); @@ -613,7 +613,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -621,12 +621,12 @@ const title = "World"; await page.keyboard.type(`{"template":{"prerender": false}}`); await app.click('button:has-text("Load"):visible'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=JavaScript'); await waitForEditorFocus(app); await page.keyboard.type(`window.livecodes.templateData = { name: 'Vento' };`); - await app.click(':nth-match([data-hint="Change Language"], 1)'); + await app.click(':nth-match([title="Change Language"], 1)'); await app.click('text=Vento'); await waitForEditorFocus(app); await page.keyboard.type(`

Welcome to {{ name }}

`); @@ -643,7 +643,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -651,7 +651,7 @@ const title = "World"; await page.keyboard.type(`{"template":{"data":{"name": "art-template"}}}`); await app.click('button:has-text("Load"):visible'); - await app.click(':nth-match([data-hint="Change Language"], 1)'); + await app.click(':nth-match([title="Change Language"], 1)'); await app.click('text=art-template'); await waitForEditorFocus(app); await page.keyboard.type(`

Welcome to {{ name }}

`); @@ -667,7 +667,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -675,12 +675,12 @@ const title = "World"; await page.keyboard.type(`{"template":{"prerender": false}}`); await app.click('button:has-text("Load"):visible'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=JavaScript'); await waitForEditorFocus(app); await page.keyboard.type(`window.livecodes.templateData = { name: 'art-template' };`); - await app.click(':nth-match([data-hint="Change Language"], 1)'); + await app.click(':nth-match([title="Change Language"], 1)'); await app.click('text=art-template'); await waitForEditorFocus(app); await page.keyboard.type(`

Welcome to {{ name }}

`); @@ -697,7 +697,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 1)'); + await app.click(':nth-match([title="Change Language"], 1)'); await app.click('text=BBCode'); await waitForEditorFocus(app); await app.page().keyboard.type('[quote]quoted text[/quote]'); @@ -713,7 +713,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 1)'); + await app.click(':nth-match([title="Change Language"], 1)'); await app.click('text=MJML'); await waitForEditorFocus(app); await page.keyboard.type(` @@ -741,7 +741,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 2)'); + await app.click(':nth-match([title="Change Language"], 2)'); await app.click('text=SCSS'); await waitForEditorFocus(app); await page.keyboard.type( @@ -759,7 +759,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 2)'); + await app.click(':nth-match([title="Change Language"], 2)'); await app.click('text=Sass'); await waitForEditorFocus(app); await page.keyboard.type(`$font-stack: Helvetica, sans-serif\nbody\n font: 100% $font-stack`); @@ -775,7 +775,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 2)'); + await app.click(':nth-match([title="Change Language"], 2)'); await app.click('text=Less'); await waitForEditorFocus(app); await page.keyboard.type(`@width: 10px; #header { width: @width; }`); @@ -791,7 +791,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 2)'); + await app.click(':nth-match([title="Change Language"], 2)'); await app.click('text=Stylus'); await waitForEditorFocus(app); await page.keyboard.insertText(`font-size = 14px\nbody\n font font-size Arial, sans-serif`); @@ -807,7 +807,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 2)'); + await app.click(':nth-match([title="Change Language"], 2)'); await app.click('text=Stylis'); await waitForEditorFocus(app); await page.keyboard.insertText( @@ -827,7 +827,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 2)'); + await app.click(':nth-match([title="Change Language"], 2)'); await app.click('text=Import Url'); await app.click('text=CSS'); await waitForEditorFocus(app); @@ -844,7 +844,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 2)'); + await app.click(':nth-match([title="Change Language"], 2)'); await app.click('text=Autoprefixer'); await app.click('text=CSS'); await waitForEditorFocus(app); @@ -861,7 +861,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 2)'); + await app.click(':nth-match([title="Change Language"], 2)'); await app.click('text=Preset Env'); await app.click('text=CSS'); await waitForEditorFocus(app); @@ -880,7 +880,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=Babel'); await waitForEditorFocus(app); await page.keyboard.insertText(`[1, 2, 3].map(n => n + 1);`); @@ -900,7 +900,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=Sucrase'); await waitForEditorFocus(app); await page.keyboard.insertText(`const Greet = (name: string) => <>Hello {name}!;`); @@ -918,7 +918,7 @@ const title = "World"; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=TypeScript'); await waitForEditorFocus(app); await page.keyboard.insertText( @@ -947,7 +947,7 @@ function isFish(pet) { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=Flow'); await waitForEditorFocus(app); await page.keyboard.insertText( @@ -967,12 +967,12 @@ function isFish(pet) { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('text=HTML'); + await app.click('text="HTML"'); await waitForEditorFocus(app); await page.keyboard.insertText(`
Loading...
`); - await app.click(':nth-match([data-hint="Change Language"], 3)'); - await app.click('text=JSX'); + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text="JSX"'); await waitForEditorFocus(app); await page.keyboard.insertText( `import React from "react"; @@ -993,8 +993,8 @@ ReactDOM.render(, document.body); const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 3)'); - await app.click('text=TSX'); + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text="TSX"'); await waitForEditorFocus(app); await page.keyboard.insertText( `import React from "react"; @@ -1016,7 +1016,7 @@ ReactDOM.render(, document.body); const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=Vue 3 SFC'); await waitForEditorFocus(app); await page.keyboard.insertText( @@ -1087,7 +1087,7 @@ h1 { await page.goto(getTestUrl()); const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=Vue 3 SFC'); await waitForEditorFocus(app); await page.keyboard.insertText(sfc); @@ -1134,7 +1134,7 @@ h1 { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=Vue 3 SFC'); await waitForEditorFocus(app); await page.keyboard.insertText(sfc); @@ -1196,7 +1196,7 @@ h1 { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=Vue 3 SFC'); await waitForEditorFocus(app); // await page.keyboard.insertText(sfc); @@ -1218,7 +1218,7 @@ h1 { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=Vue 3 SFC'); await waitForEditorFocus(app); await page.keyboard.insertText( @@ -1230,7 +1230,7 @@ h1 { `, ); - await app.click(':nth-match([data-hint="Change Language"], 2)'); + await app.click(':nth-match([title="Change Language"], 2)'); await app.click('text=Tailwind CSS'); await app.click('text=CSS'); await waitForEditorFocus(app); @@ -1255,7 +1255,7 @@ h1 { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=Vue 2'); await waitForEditorFocus(app); await page.keyboard.insertText( @@ -1284,7 +1284,7 @@ h1 { color: blue; } const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=Svelte'); await waitForEditorFocus(app); await page.keyboard.insertText( @@ -1314,7 +1314,7 @@ h1 { color: blue; } const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=Malina.js'); await waitForEditorFocus(app); await page.keyboard.insertText( @@ -1346,11 +1346,11 @@ h1 { color: blue; } const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('text=HTML'); + await app.click('text="HTML"'); await waitForEditorFocus(app); await page.keyboard.insertText(''); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=Stencil'); await waitForEditorFocus(app); await page.keyboard.insertText( @@ -1381,7 +1381,7 @@ export class App { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=CoffeeScript'); await waitForEditorFocus(app); await page.keyboard.insertText(`square = (x) -> x * x`); @@ -1402,11 +1402,11 @@ export class App { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('text=HTML'); + await app.click('text="HTML"'); await waitForEditorFocus(app); await page.keyboard.insertText('

Hello, World

'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=LiveScript'); await waitForEditorFocus(app); await page.keyboard.insertText(`{ capitalize, join, map, words } = require 'prelude-ls' @@ -1429,11 +1429,11 @@ title = 'live script' const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('text=HTML'); + await app.click('text="HTML"'); await waitForEditorFocus(app); await page.keyboard.insertText(''); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=Riot.js'); await waitForEditorFocus(app); await page.keyboard.insertText('

Hello, {props.title}

'); @@ -1451,7 +1451,7 @@ title = 'live script' const { app, getResult } = await getLoadedApp(page); - await app.click('text=HTML'); + await app.click('text="HTML"'); await waitForEditorFocus(app); await page.keyboard.insertText( `

Hello, World

@@ -1465,7 +1465,7 @@ title = 'live script' `, ); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=AssemblyScript'); await waitForEditorFocus(app); await page.keyboard.insertText(`export function getTitle(): string {return "AssemblyScript";`); @@ -1486,11 +1486,11 @@ title = 'live script' const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('text=HTML'); + await app.click('text="HTML"'); await waitForEditorFocus(app); await page.keyboard.insertText('

Hello, World

'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=Python'); await waitForEditorFocus(app); await page.keyboard.insertText(`from browser import document @@ -1509,11 +1509,11 @@ document['header'].html = f"Hello, {title}"`); const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('text=HTML'); + await app.click('text="HTML"'); await waitForEditorFocus(app); await page.keyboard.insertText('

Hello, World

'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=Pyodide'); await waitForEditorFocus(app); await page.keyboard.insertText(`from js import document @@ -1534,11 +1534,11 @@ document.getElementById('header').innerHTML = f"Hello, {title}"`); const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('text=HTML'); + await app.click('text="HTML"'); await waitForEditorFocus(app); await page.keyboard.insertText('

Hello, world

'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=Ruby'); await waitForEditorFocus(app); @@ -1559,11 +1559,11 @@ $$.document.querySelector('#title').innerHTML = title`); const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('text=HTML'); + await app.click('text="HTML"'); await waitForEditorFocus(app); await page.keyboard.insertText('

Hello, world

'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=Go'); await waitForEditorFocus(app); @@ -1586,11 +1586,11 @@ func main() { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('text=HTML'); + await app.click('text="HTML"'); await waitForEditorFocus(app); await page.keyboard.insertText('

Hello, world

'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=PHP'); await waitForEditorFocus(app); @@ -1614,11 +1614,11 @@ $document->getElementById('title')->textContent = $title;`, const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('text=HTML'); + await app.click('text="HTML"'); await waitForEditorFocus(app); await page.keyboard.insertText('

Hello, world

'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=Perl'); await waitForEditorFocus(app); @@ -1639,11 +1639,11 @@ JS::inline('document.getElementById("title").innerHTML') = $title;`); const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('text=HTML'); + await app.click('text="HTML"'); await waitForEditorFocus(app); await page.keyboard.insertText('

Hello, world

'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=Lua'); await waitForEditorFocus(app); @@ -1665,11 +1665,11 @@ document:getElementById("title").innerHTML = "Lua"`); const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('text=HTML'); + await app.click('text="HTML"'); await waitForEditorFocus(app); await page.keyboard.insertText('

Hello, world

'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=Scheme'); await waitForEditorFocus(app); diff --git a/e2e/specs/css-modules.spec.ts b/e2e/specs/css-modules.spec.ts index 876893218..154eced30 100644 --- a/e2e/specs/css-modules.spec.ts +++ b/e2e/specs/css-modules.spec.ts @@ -8,19 +8,19 @@ test.describe('CSS Modules', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 1)'); - await app.click('text=HTML'); + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text="HTML"'); await waitForEditorFocus(app); await page.keyboard.type('

'); - await app.click(':nth-match([data-hint="Change Language"], 2)'); - await app.click('text=CSS Modules'); - await app.click('text=CSS'); + await app.click(':nth-match([title="Change Language"], 2)'); + await app.click('text="CSS Modules"'); + await app.click('text="CSS"'); await waitForEditorFocus(app); await page.keyboard.type('.my-title {color: blue;}'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); - await app.click('text=JavaScript'); + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text="JavaScript"'); await waitForEditorFocus(app); await page.keyboard.type( `import classes from './style.module.css'; @@ -42,19 +42,19 @@ document.querySelector('h1').className = classes['my-title']; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 1)'); - await app.click('text=HTML'); + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text="HTML"'); await waitForEditorFocus(app); await page.keyboard.type('

'); - await app.click(':nth-match([data-hint="Change Language"], 2)'); - await app.click('text=CSS Modules'); - await app.click('text=CSS'); + await app.click(':nth-match([title="Change Language"], 2)'); + await app.click('text="CSS Modules"'); + await app.click('text="CSS"'); await waitForEditorFocus(app); await page.keyboard.type('.my-title {color: blue;}'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); - await app.click('text=JavaScript'); + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text="JavaScript"'); await waitForEditorFocus(app); await page.keyboard.type( `import { myTitle } from './styles.module.css'; @@ -76,19 +76,19 @@ document.querySelector('h1').className = myTitle; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 1)'); - await app.click('text=HTML'); + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text="HTML"'); await waitForEditorFocus(app); await page.keyboard.type('

'); - await app.click(':nth-match([data-hint="Change Language"], 2)'); - await app.click('text=CSS Modules'); - await app.click('text=CSS'); + await app.click(':nth-match([title="Change Language"], 2)'); + await app.click('text="CSS Modules"'); + await app.click('text="CSS"'); await waitForEditorFocus(app); await page.keyboard.type('.my-title {color: blue;}'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); - await app.click('text=JavaScript'); + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text="JavaScript"'); await waitForEditorFocus(app); await page.keyboard.type( `import * as classes from './style.module.css'; @@ -110,18 +110,18 @@ document.querySelector('h1').className = classes.myTitle; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 1)'); - await app.click('text=HTML'); + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text="HTML"'); await waitForEditorFocus(app); await page.keyboard.type('

'); - await app.click(':nth-match([data-hint="Change Language"], 2)'); - await app.click('text=CSS'); + await app.click(':nth-match([title="Change Language"], 2)'); + await app.click('text="CSS"'); await waitForEditorFocus(app); await page.keyboard.type('.my-title {color: blue;}'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); - await app.click('text=JavaScript'); + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text="JavaScript"'); await waitForEditorFocus(app); await page.keyboard.type( `import classes from './style.css'; document.querySelector('h1').innerHTML = classes;`, @@ -138,19 +138,19 @@ document.querySelector('h1').className = classes.myTitle; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 1)'); - await app.click('text=HTML'); + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text="HTML"'); await waitForEditorFocus(app); await page.keyboard.type('

'); - await app.click(':nth-match([data-hint="Change Language"], 2)'); - await app.click('text=CSS Modules'); - await app.click('text=CSS'); + await app.click(':nth-match([title="Change Language"], 2)'); + await app.click('text="CSS Modules"'); + await app.click('text="CSS"'); await waitForEditorFocus(app); await page.keyboard.type('.my-title {color: blue;}'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); - await app.click('text=JavaScript'); + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text="JavaScript"'); await waitForEditorFocus(app); await page.keyboard.type( ` @@ -174,16 +174,16 @@ document.querySelector('h1').className = classes2['my-title']; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 1)'); - await app.click('text=HTML'); + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text="HTML"'); await waitForEditorFocus(app); await page.keyboard.type('

'); - await app.click(':nth-match([data-hint="Change Language"], 2)'); - await app.click('text=CSS Modules'); + await app.click(':nth-match([title="Change Language"], 2)'); + await app.click('text="CSS Modules"'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); - await app.click('text=JavaScript'); + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text="JavaScript"'); await waitForEditorFocus(app); await page.keyboard.type( ` @@ -203,18 +203,18 @@ document.querySelector('h1').innerHTML = Object.keys(classes).length; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 1)'); - await app.click('text=HTML'); + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text="HTML"'); await waitForEditorFocus(app); await page.keyboard.type('

'); - await app.click(':nth-match([data-hint="Change Language"], 2)'); - await app.click('text=CSS'); + await app.click(':nth-match([title="Change Language"], 2)'); + await app.click('text="CSS"'); await waitForEditorFocus(app); await page.keyboard.type('.my-title {color: blue;}'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); - await app.click('text=JavaScript'); + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text="JavaScript"'); await waitForEditorFocus(app); await page.keyboard.type( ` @@ -234,20 +234,20 @@ document.querySelector('h1').innerHTML = Object.keys(classes).length; const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 1)'); - await app.click('text=HTML'); + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text="HTML"'); await waitForEditorFocus(app); await page.keyboard.type('

'); - await app.click(':nth-match([data-hint="Change Language"], 2)'); - await app.click('text=CSS Modules'); + await app.click(':nth-match([title="Change Language"], 2)'); + await app.click('text="CSS Modules"'); await app.click('text=cssnano'); await app.click('text=SCSS'); await waitForEditorFocus(app); await page.keyboard.type('h1 { &.my-title {color: blue;} }'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); - await app.click('text=JavaScript'); + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text="JavaScript"'); await waitForEditorFocus(app); await page.keyboard.type( `import classes from './style.module.scss'; diff --git a/e2e/specs/custom-settings.spec.ts b/e2e/specs/custom-settings.spec.ts index 4524df51e..fdc00b7ef 100644 --- a/e2e/specs/custom-settings.spec.ts +++ b/e2e/specs/custom-settings.spec.ts @@ -8,7 +8,7 @@ test.describe('Custom Settings', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -16,7 +16,7 @@ test.describe('Custom Settings', () => { await page.keyboard.type(`{"asciidoc": { standalone: true }}`); await app.click('button:has-text("Load"):visible'); - await app.click(':nth-match([data-hint="Change Language"], 1)'); + await app.click(':nth-match([title="Change Language"], 1)'); await app.click('text=AsciiDoc'); await waitForEditorFocus(app); await page.keyboard.type('hello'); @@ -31,7 +31,7 @@ test.describe('Custom Settings', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -39,7 +39,7 @@ test.describe('Custom Settings', () => { await page.keyboard.type(`{"scss": {"style": "compressed"}}`); await app.click('button:has-text("Load"):visible'); - await app.click(':nth-match([data-hint="Change Language"], 2)'); + await app.click(':nth-match([title="Change Language"], 2)'); await app.click('text=SCSS'); await waitForEditorFocus(app); await page.keyboard.type( @@ -58,7 +58,7 @@ test.describe('Custom Settings', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -66,7 +66,7 @@ test.describe('Custom Settings', () => { await page.keyboard.insertText(`{"sass": {"style": "compressed"}}`); await app.click('button:has-text("Load"):visible'); - await app.click(':nth-match([data-hint="Change Language"], 2)'); + await app.click(':nth-match([title="Change Language"], 2)'); await app.click('text=Sass'); await waitForEditorFocus(app); await page.keyboard.insertText(` @@ -88,7 +88,7 @@ body const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -96,7 +96,7 @@ body await page.keyboard.type(`{"less": {"math": "always"}}`); await app.click('button:has-text("Load"):visible'); - await app.click(':nth-match([data-hint="Change Language"], 2)'); + await app.click(':nth-match([title="Change Language"], 2)'); await app.click('text=Less'); await waitForEditorFocus(app); await page.keyboard.type(`.math { a: 1 + 1; b: 2px / 2; c: 2px ./ 2; d: (2px / 2); }`); @@ -111,7 +111,7 @@ body const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -134,7 +134,7 @@ body const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -142,7 +142,7 @@ body await page.keyboard.type(`{"autoprefixer": {"add": false}}`); await app.click('button:has-text("Load"):visible'); - await app.click(':nth-match([data-hint="Change Language"], 2)'); + await app.click(':nth-match([title="Change Language"], 2)'); await app.click('text=autoprefixer'); await app.click('text=CSS'); await waitForEditorFocus(app); @@ -158,7 +158,7 @@ body const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -166,7 +166,7 @@ body await page.keyboard.type(`{"postcssPresetEnv": {"stage": 3}}`); await app.click('button:has-text("Load"):visible'); - await app.click(':nth-match([data-hint="Change Language"], 2)'); + await app.click(':nth-match([title="Change Language"], 2)'); await app.click('text=Preset Env'); await app.click('text=CSS'); await waitForEditorFocus(app); @@ -182,7 +182,7 @@ body const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -192,11 +192,11 @@ body ); await app.click('button:has-text("Load"):visible'); - await app.click('text=HTML'); + await app.click('text="HTML"'); await waitForEditorFocus(app); await page.keyboard.type('Hello'); - await app.click(':nth-match([data-hint="Change Language"], 2)'); + await app.click(':nth-match([title="Change Language"], 2)'); await app.click('text=Tailwind CSS'); await app.click('text=CSS'); await waitForEditorFocus(app); @@ -217,7 +217,7 @@ body const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -227,11 +227,11 @@ body ); await app.click('button:has-text("Load"):visible'); - await app.click('text=HTML'); + await app.click('text="HTML"'); await waitForEditorFocus(app); await page.keyboard.type('Hello'); - await app.click(':nth-match([data-hint="Change Language"], 2)'); + await app.click(':nth-match([title="Change Language"], 2)'); await app.click('text=Windi CSS'); await app.click('text=CSS'); await waitForEditorFocus(app); @@ -251,7 +251,7 @@ body const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -259,7 +259,7 @@ body await page.keyboard.type(`{"babel": {"sourceMaps": "inline"}}`); await app.click('button:has-text("Load"):visible'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=Babel'); await waitForEditorFocus(app); await page.keyboard.type('const x = () => "hi";\n'); @@ -274,7 +274,7 @@ body const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -282,7 +282,7 @@ body await page.keyboard.type(`{"typescript": {"target": "es5"}}`); await app.click('button:has-text("Load"):visible'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=TypeScript'); await waitForEditorFocus(app); await page.keyboard.type('const x = () => "hi";\n'); @@ -297,7 +297,7 @@ body const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -305,7 +305,7 @@ body await page.keyboard.type(`{"flow": {"pretty": true}}`); await app.click('button:has-text("Load"):visible'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=Flow'); await waitForEditorFocus(app); await page.keyboard.insertText( @@ -324,7 +324,7 @@ body const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -344,7 +344,7 @@ body const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -362,7 +362,7 @@ body const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -370,7 +370,7 @@ body await page.keyboard.type(`{"coffeescript": {"bare": false}}`); await app.click('button:has-text("Load"):visible'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=CoffeeScript'); await waitForEditorFocus(app); await page.keyboard.type('x = 10'); @@ -385,7 +385,7 @@ body const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); @@ -393,7 +393,7 @@ body await page.keyboard.type(`{"livescript": {"bare": false}}`); await app.click('button:has-text("Load"):visible'); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=LiveScript'); await waitForEditorFocus(app); await page.keyboard.type('x = 10'); diff --git a/e2e/specs/i18n.spec.ts b/e2e/specs/i18n.spec.ts index 15c69a3b5..424860f16 100644 --- a/e2e/specs/i18n.spec.ts +++ b/e2e/specs/i18n.spec.ts @@ -78,9 +78,9 @@ test.describe('I18n', () => { }); }); - test.describe('Check Embed (Run Button)', () => { + test.describe.skip('Check Embed (Run Button)', () => { const checkRunButtonEmbed = (text: string, appLanguage?: AppLanguage) => - checkText('#run-button', text, 'data-hint', appLanguage, { embed: true }); + checkText('#run-button', text, 'title', appLanguage, { embed: true }); test('Check default fallback (en)', checkRunButtonEmbed('Run (Shift + Enter)')); test( diff --git a/e2e/specs/import-map.spec.ts b/e2e/specs/import-map.spec.ts index cccc6799f..5958c93aa 100644 --- a/e2e/specs/import-map.spec.ts +++ b/e2e/specs/import-map.spec.ts @@ -8,7 +8,7 @@ test.describe('Import maps', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=JavaScript'); await waitForEditorFocus(app); await page.keyboard.type( @@ -26,7 +26,7 @@ test.describe('Import maps', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=JavaScript'); await waitForEditorFocus(app); await page.keyboard.type("import {v4} from 'uuid';\ndocument.body.innerText = v4()"); @@ -42,7 +42,7 @@ test.describe('Import maps', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=JavaScript'); await waitForEditorFocus(app); await page.keyboard.type("import {v4} from 'skypack:uuid';\ndocument.body.innerText = v4()"); @@ -58,7 +58,7 @@ test.describe('Import maps', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=JavaScript'); await waitForEditorFocus(app); await page.keyboard.type("const {v4} = require('uuid');\ndocument.body.innerText = v4()"); @@ -74,7 +74,7 @@ test.describe('Import maps', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=JavaScript'); await waitForEditorFocus(app); await page.keyboard.type( @@ -92,7 +92,7 @@ test.describe('Import maps', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=JavaScript'); await waitForEditorFocus(app); await page.keyboard.type( @@ -110,7 +110,7 @@ test.describe('Import maps', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=JavaScript'); await waitForEditorFocus(app); await page.keyboard.type( @@ -128,7 +128,7 @@ test.describe('Import maps', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=JavaScript'); await waitForEditorFocus(app); await page.keyboard.type( @@ -146,7 +146,7 @@ test.describe('Import maps', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=JavaScript'); await waitForEditorFocus(app); await page.keyboard.type( @@ -164,7 +164,7 @@ test.describe('Import maps', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([data-hint="Change Language"], 3)'); + await app.click(':nth-match([title="Change Language"], 3)'); await app.click('text=JavaScript'); await waitForEditorFocus(app); await page.keyboard.type( diff --git a/e2e/specs/import.spec.ts b/e2e/specs/import.spec.ts index 14c8950eb..79bea1db9 100644 --- a/e2e/specs/import.spec.ts +++ b/e2e/specs/import.spec.ts @@ -38,7 +38,7 @@ test.describe('Import from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text="Import …"'); await app.fill('#code-url', url); await app.click('button:has-text("Import from URL"):visible'); @@ -55,7 +55,7 @@ test.describe('Import from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text="Import …"'); await app.fill('#code-url', githubRepo); await app.click('button:has-text("Import from URL"):visible'); @@ -71,7 +71,7 @@ test.describe('Import from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text="Import …"'); await app.fill('#code-url', githubFile); await app.click('button:has-text("Import from URL"):visible'); @@ -86,7 +86,7 @@ test.describe('Import from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text="Import …"'); await app.fill('#code-url', gitlabRepo); await app.click('button:has-text("Import from URL"):visible'); @@ -101,7 +101,7 @@ test.describe('Import from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text="Import …"'); await app.fill('#code-url', gitlabFile); await app.click('button:has-text("Import from URL"):visible'); @@ -116,7 +116,7 @@ test.describe('Import from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text="Import …"'); await app.fill('#code-url', rawCode); await app.click('button:has-text("Import from URL"):visible'); @@ -131,7 +131,7 @@ test.describe('Import from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text="Import …"'); await app.click('text=Import Project JSON'); await app.fill('#json-url', jsonURL); @@ -147,7 +147,7 @@ test.describe('Import from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text="Import …"'); await app.fill( '#code-url', diff --git a/e2e/specs/sfc-processors.spec.ts b/e2e/specs/sfc-processors.spec.ts index 6d1d7d5fb..244c09f4e 100644 --- a/e2e/specs/sfc-processors.spec.ts +++ b/e2e/specs/sfc-processors.spec.ts @@ -104,7 +104,7 @@ demo const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=Custom Settings'); await waitForEditorFocus(app, '#custom-settings-editor'); await page.keyboard.press('Control+A'); diff --git a/e2e/specs/starter.spec.ts b/e2e/specs/starter.spec.ts index 2a3d4ebd4..1de706b64 100644 --- a/e2e/specs/starter.spec.ts +++ b/e2e/specs/starter.spec.ts @@ -7,7 +7,7 @@ const templates = [ 'TypeScript', 'React', 'Angular', - 'Preact', + // 'Preact', 'Svelte', 'Solid', 'Lit', @@ -39,7 +39,7 @@ test.describe('Starter Templates from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=New'); await app.click('text=Blank'); await waitForEditorFocus(app); @@ -56,7 +56,7 @@ test.describe('Starter Templates from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=New'); await app.click(`text=${template} Starter`); await waitForEditorFocus(app); @@ -80,7 +80,7 @@ test.describe('Starter Templates from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=New'); await app.click('text=React Native Starter'); await waitForEditorFocus(app); @@ -103,7 +103,7 @@ test.describe('Starter Templates from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=New'); await app.click('text=Vue 2 Starter'); await waitForEditorFocus(app); @@ -126,7 +126,7 @@ test.describe('Starter Templates from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=New'); await app.click('text=Vue 3 SFC Starter'); await waitForEditorFocus(app); @@ -149,7 +149,7 @@ test.describe('Starter Templates from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=New'); await app.click('text=Riot.js Starter'); await waitForEditorFocus(app); @@ -172,7 +172,7 @@ test.describe('Starter Templates from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=New'); await app.click('text=Malina.js Starter'); await waitForEditorFocus(app); @@ -195,7 +195,7 @@ test.describe('Starter Templates from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=New'); await app.click('text=Imba Starter'); await waitForEditorFocus(app); @@ -223,7 +223,7 @@ test.describe('Starter Templates from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=New'); await app.click('text=Ruby (Wasm) Starter'); @@ -249,7 +249,7 @@ test.describe('Starter Templates from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=New'); await app.click('text=D3 Starter'); @@ -267,7 +267,7 @@ test.describe('Starter Templates from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=New'); await app.click('text=Phaser Starter'); @@ -285,7 +285,7 @@ test.describe('Starter Templates from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=New'); await app.click('text=Lua (Wasm) Starter'); @@ -311,7 +311,7 @@ test.describe('Starter Templates from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=New'); await app.click('text=PHP (Wasm) Starter'); @@ -330,7 +330,7 @@ test.describe('Starter Templates from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=New'); await app.click('text=Go Starter'); await waitForEditorFocus(app); @@ -354,7 +354,7 @@ test.describe('Starter Templates from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=New'); await app.click('text=C++ Starter'); await waitForEditorFocus(app); @@ -379,7 +379,7 @@ test.describe('Starter Templates from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=New'); await app.click('text=ReScript Starter'); await waitForEditorFocus(app); @@ -404,7 +404,7 @@ test.describe('Starter Templates from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=New'); await app.click('text=Reason Starter'); await waitForEditorFocus(app); @@ -429,7 +429,7 @@ test.describe('Starter Templates from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=New'); await app.click('text=OCaml Starter'); await waitForEditorFocus(app); @@ -452,7 +452,7 @@ test.describe('Starter Templates from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=New'); await app.click('text=Common Lisp Starter'); await waitForEditorFocus(app); @@ -475,7 +475,7 @@ test.describe('Starter Templates from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=New'); await app.click('text=AssemblyScript Starter'); await waitForEditorFocus(app); @@ -498,7 +498,7 @@ test.describe('Starter Templates from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=New'); await app.click('text=WebAssembly Text Starter'); await waitForEditorFocus(app); @@ -521,7 +521,7 @@ test.describe('Starter Templates from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=New'); await app.click('text=Bootstrap Starter'); await waitForEditorFocus(app); @@ -537,7 +537,7 @@ test.describe('Starter Templates from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=New'); await app.click('text=Tailwind CSS Starter'); await waitForEditorFocus(app); @@ -553,7 +553,7 @@ test.describe('Starter Templates from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=New'); await app.click('text=Markdown Starter'); await waitForEditorFocus(app); @@ -569,7 +569,7 @@ test.describe('Starter Templates from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=New'); await app.click('text=SQL Starter'); await waitForEditorFocus(app); @@ -585,7 +585,7 @@ test.describe('Starter Templates from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=New'); await app.click('text=PostgreSQL Starter'); await waitForEditorFocus(app); @@ -601,7 +601,7 @@ test.describe('Starter Templates from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=New'); await app.click('text=Prolog Starter'); await waitForEditorFocus(app); @@ -622,7 +622,7 @@ test.describe('Starter Templates from UI', () => { const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click('[aria-label="Menu"]'); + await app.click('[aria-label="Project"]'); await app.click('text=New'); await app.click(`text=Blockly Starter`); await waitForEditorFocus(app); diff --git a/functions/oembed.ts b/functions/oembed.ts index 9fafe8b13..9c7172504 100644 --- a/functions/oembed.ts +++ b/functions/oembed.ts @@ -50,7 +50,7 @@ export const onRequest: PgFunction = async function (context) { src="${url.href}" scrolling="no" height="300" - style="border: 1px solid black; border-radius: 5px; width: 100%;${ + style="border: 1px solid black; border-radius: 6px; width: 100%;${ maxWidth ? ' max-width: ' + maxWidth + 'px;' : '' }${maxHeight ? ' max-height: ' + maxHeight + 'px;' : ''}" >`, diff --git a/images/logo/livecodes-logo-border.svg b/images/logo/livecodes-logo-border.svg new file mode 100644 index 000000000..50dc9e368 --- /dev/null +++ b/images/logo/livecodes-logo-border.svg @@ -0,0 +1 @@ + diff --git a/images/logo/livecodes-logo-icon-border.svg b/images/logo/livecodes-logo-icon-border.svg new file mode 100644 index 000000000..50dc9e368 --- /dev/null +++ b/images/logo/livecodes-logo-icon-border.svg @@ -0,0 +1 @@ + diff --git a/images/logo/livecodes-logo-icon.svg b/images/logo/livecodes-logo-icon.svg index bd43fc4e7..cd431ddec 100644 --- a/images/logo/livecodes-logo-icon.svg +++ b/images/logo/livecodes-logo-icon.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/images/logo/livecodes-logo.svg b/images/logo/livecodes-logo.svg index 0ca27dc6e..411bd86ce 100644 --- a/images/logo/livecodes-logo.svg +++ b/images/logo/livecodes-logo.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/scripts/build.js b/scripts/build.js index 08b7c57c2..b339dc8d2 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -1,307 +1,307 @@ -const esbuild = require('esbuild'); -const minifyHTML = require('esbuild-plugin-minify-html').default; -const fs = require('fs'); -const path = require('path'); - -const { applyHash } = require('./hash'); -const { injectCss } = require('./inject-css'); -const { buildVendors } = require('./vendors'); -const { buildStyles } = require('./styles'); -const { buildI18n, buildLocalePathLoader } = require('./i18n'); -const { arrToObj, mkdir, uint8arrayToString, iife, getFileNames, getEnvVars } = require('./utils'); - -const args = process.argv.slice(2); -const devMode = args.includes('--dev'); -const outDir = path.resolve(__dirname + '/../build'); - -const prepareDir = async () => { - mkdir(outDir); - mkdir(outDir + '/livecodes/'); - mkdir(outDir + '/sdk/'); - if (devMode) { - mkdir(outDir + '/tmp/'); - } - const fileNames = await getFileNames(outDir + '/livecodes/'); - await Promise.all(fileNames.map(async (f) => fs.promises.unlink(outDir + '/livecodes/' + f))); - - if (process.env.CF_PAGES) { - // add headers in Cloudflare - await fs.promises.copyFile( - path.resolve(__dirname + '/../src/_headers'), - path.resolve(outDir + '/_headers'), - ); - } - await fs.promises.copyFile( - path.resolve(__dirname + '/../src/favicon.ico'), - path.resolve(outDir + '/favicon.ico'), - ); - await fs.promises.copyFile( - path.resolve(__dirname + '/../src/404.html'), - path.resolve(outDir + '/404.html'), - ); - await fs.promises.copyFile( - path.resolve(__dirname + '/../src/index.html'), - path.resolve(outDir + '/index.html'), - ); - await fs.promises.copyFile( - path.resolve(__dirname + '/../src/livecodes/html/app-base.html'), - path.resolve(outDir + '/app.html'), - ); -}; - -/** @type {Partial} */ -const baseOptions = { - bundle: true, - minify: devMode ? false : true, - outdir: 'build/livecodes', - format: 'esm', - target: 'es2020', - sourcemap: false, - sourcesContent: true, - define: { - ...getEnvVars(devMode), - }, - loader: { '.html': 'text', '.ttf': 'file' }, - logLevel: 'error', - external: ['@codemirror/*', '@lezer/*'], - plugins: [ - ...(devMode - ? [] - : [ - minifyHTML({ - collapseWhitespace: true, - collapseBooleanAttributes: true, - minifyJS: true, - minifyCSS: true, - processScripts: ['importmap'], - }), - ]), - ], -}; - -const sdkBuild = () => { - const sdkSrcDir = 'src/sdk/'; - const sdkSrcMod = sdkSrcDir + 'index.ts'; - const sdkOutDir = 'build/sdk/'; - - fs.copyFileSync(path.resolve('LICENSE'), path.resolve(sdkOutDir + 'LICENSE')); - fs.copyFileSync(path.resolve('README.md'), path.resolve(sdkOutDir + 'README.md')); - fs.copyFileSync( - path.resolve(sdkSrcDir + 'package.sdk.json'), - path.resolve(sdkOutDir + 'package.json'), - ); - - const sdkOptions = { - ...baseOptions, - target: 'es2018', - outdir: undefined, - }; - - return Promise.all([ - esbuild.build({ - ...sdkOptions, - entryPoints: [sdkSrcMod], - outdir: undefined, - outfile: sdkOutDir + 'livecodes.js', - }), - esbuild.build({ - ...sdkOptions, - entryPoints: [sdkSrcMod], - outdir: undefined, - outfile: sdkOutDir + 'livecodes.cjs', - format: 'cjs', - }), - esbuild.build({ - ...sdkOptions, - entryPoints: [sdkSrcMod], - outdir: undefined, - outfile: sdkOutDir + 'livecodes.umd.js', - format: 'iife', - globalName: 'livecodes', - }), - esbuild.build({ - ...sdkOptions, - entryPoints: [sdkSrcDir + 'react.tsx'], - outdir: undefined, - outfile: sdkOutDir + 'react.js', - external: ['react'], - jsx: 'automatic', - }), - esbuild.build({ - ...sdkOptions, - entryPoints: [sdkSrcDir + 'vue.ts'], - outdir: undefined, - outfile: sdkOutDir + 'vue.js', - external: ['vue'], - alias: { - '@vue/runtime-core': 'vue', - }, - }), - ]); -}; - -const esmBuild = () => - esbuild.build({ - ...baseOptions, - entryPoints: [ - 'app.ts', - 'embed.ts', - 'lite.ts', - 'headless.ts', - 'templates/starter/index.ts', - 'editor/monaco/monaco.ts', - 'editor/codemirror/codemirror.ts', - 'editor/codejar/codejar.ts', - 'editor/blockly/blockly.ts', - 'editor/quill/quill.ts', - 'import/import-src.ts', - 'services/firebase.ts', - 'services/google-fonts.ts', - 'languages/language-info.ts', - 'export/export.ts', - 'sync/sync.ts', - 'types/bundle-types.ts', - 'UI/open.ts', - 'UI/resources.ts', - 'UI/assets.ts', - 'UI/snippets.ts', - 'UI/backup.ts', - 'UI/broadcast.ts', - 'UI/import.ts', - 'UI/share.ts', - 'UI/deploy.ts', - 'UI/sync-ui.ts', - 'UI/embed-ui.ts', - 'UI/editor-settings.ts', - 'languages/diagrams/lang-diagrams-compiler-esm.ts', - 'languages/postgresql/lang-postgresql-compiler-esm.ts', - 'languages/r/lang-r-script-esm.ts', - 'languages/rescript/lang-rescript-compiler-esm.ts', - 'i18n/i18n.ts', - ] - .map((x) => 'src/livecodes/' + x) - .reduce(arrToObj, {}), - }); - -const iifeBuild = () => - esbuild.build({ - ...baseOptions, - format: 'iife', - entryPoints: [ - 'index.ts', - 'compiler/compile.page.ts', - 'compiler/compiler-utils.ts', - 'editor/custom-editor-utils.ts', - 'result/result-utils.ts', - 'languages/art-template/lang-art-template-compiler.ts', - 'languages/assemblyscript/lang-assemblyscript-script.ts', - 'languages/assemblyscript/lang-assemblyscript-compiler.ts', - 'languages/astro/lang-astro-compiler.ts', - 'languages/clio/lang-clio-compiler.ts', - 'languages/commonlisp/lang-commonlisp-script.ts', - 'languages/cpp/lang-cpp-script.ts', - 'languages/cpp-wasm/lang-cpp-wasm-script.ts', - 'languages/dot/lang-dot-compiler.ts', - 'languages/ejs/lang-ejs-compiler.ts', - 'languages/eta/lang-eta-compiler.ts', - 'languages/haml/lang-haml-compiler.ts', - 'languages/handlebars/lang-handlebars-compiler.ts', - 'languages/imba/lang-imba-compiler.ts', - 'languages/julia/lang-julia-script.ts', - 'languages/liquid/lang-liquid-compiler.ts', - 'languages/lua-wasm/lang-lua-wasm-script.ts', - 'languages/malina/lang-malina-compiler.ts', - 'languages/mustache/lang-mustache-compiler.ts', - 'languages/nunjucks/lang-nunjucks-compiler.ts', - 'languages/perl/lang-perl-script.ts', - 'languages/php-wasm/lang-php-wasm-script.ts', - 'languages/prolog/lang-prolog-script.ts', - 'languages/pug/lang-pug-compiler.ts', - 'languages/python-wasm/lang-python-wasm-script.ts', - 'languages/rescript/lang-rescript-formatter.ts', - 'languages/riot/lang-riot-compiler.ts', - 'languages/ruby-wasm/lang-ruby-wasm-script.ts', - 'languages/scss/lang-scss-compiler.ts', - 'languages/solid/lang-solid-compiler.ts', - 'languages/sql/lang-sql-compiler.ts', - 'languages/sql/lang-sql-script.ts', - 'languages/svelte/lang-svelte-compiler.ts', - 'languages/tcl/lang-tcl-script.ts', - 'languages/twig/lang-twig-compiler.ts', - 'languages/vento/lang-vento-compiler.ts', - 'languages/vue/lang-vue-compiler.ts', - 'languages/vue2/lang-vue2-compiler.ts', - 'languages/wat/lang-wat-compiler.ts', - 'languages/wat/lang-wat-script.ts', - 'languages/teal/lang-teal-compiler.ts', - 'languages/fennel/lang-fennel-compiler.ts', - 'languages/gleam/lang-gleam-compiler.ts', - 'languages/tailwindcss/processor-tailwindcss-compiler.ts', - 'languages/windicss/processor-windicss-compiler.ts', - 'languages/unocss/processor-unocss-compiler.ts', - 'languages/lightningcss/processor-lightningcss-compiler.ts', - 'languages/postcss/processor-postcss-compiler.ts', - ] - .map((x) => 'src/livecodes/' + x) - .reduce(arrToObj, {}), - }); - -/** @type {Partial} */ -const workerOptions = { - ...baseOptions, - entryPoints: [ - 'src/livecodes/compiler/compile.worker.ts', - 'src/livecodes/formatter/format.worker.ts', - 'src/livecodes/sync/sync.worker.ts', - ], - write: false, -}; - -const workersBuild = () => - esbuild.build(workerOptions).then((worker) => { - for (let out of worker.outputFiles || []) { - const content = uint8arrayToString(out.contents); - const filename = path.basename(out.path); - fs.writeFile( - path.resolve('build/livecodes', filename), - filename.endsWith('.map') ? content : iife(content), - () => { }, - ); - } - }); - -const functionsBuild = () => - esbuild.build({ - ...baseOptions, - outdir: 'functions/build', - entryPoints: ['src/livecodes/utils/compression.ts'], - }); - -const stylesBuild = () => buildStyles(devMode); - -prepareDir().then(async () => { - await buildLocalePathLoader(); - Promise.all([ - esmBuild(), - iifeBuild(), - workersBuild(), - stylesBuild(), - sdkBuild(), - buildI18n(), - ]).then(async () => { - if (!devMode) { - buildVendors(); - functionsBuild(); - } - await applyHash({ devMode }); - await injectCss(); - if (devMode) { - fs.writeFileSync( - path.resolve('build/tmp/trigger-reload.txt'), - new Date().toISOString(), - 'utf8', - ); - } - console.log('built to: ' + baseOptions.outdir + '/'); - }); -}); +const esbuild = require('esbuild'); +const minifyHTML = require('esbuild-plugin-minify-html').default; +const fs = require('fs'); +const path = require('path'); + +const { applyHash } = require('./hash'); +const { injectCss } = require('./inject-css'); +const { buildVendors } = require('./vendors'); +const { buildStyles } = require('./styles'); +const { buildI18n, buildLocalePathLoader } = require('./i18n'); +const { arrToObj, mkdir, uint8arrayToString, iife, getFileNames, getEnvVars } = require('./utils'); + +const args = process.argv.slice(2); +const devMode = args.includes('--dev'); +const outDir = path.resolve(__dirname + '/../build'); + +const prepareDir = async () => { + mkdir(outDir); + mkdir(outDir + '/livecodes/'); + mkdir(outDir + '/sdk/'); + if (devMode) { + mkdir(outDir + '/tmp/'); + } + const fileNames = await getFileNames(outDir + '/livecodes/'); + await Promise.all(fileNames.map(async (f) => fs.promises.unlink(outDir + '/livecodes/' + f))); + + if (process.env.CF_PAGES) { + // add headers in Cloudflare + await fs.promises.copyFile( + path.resolve(__dirname + '/../src/_headers'), + path.resolve(outDir + '/_headers'), + ); + } + await fs.promises.copyFile( + path.resolve(__dirname + '/../src/favicon.ico'), + path.resolve(outDir + '/favicon.ico'), + ); + await fs.promises.copyFile( + path.resolve(__dirname + '/../src/404.html'), + path.resolve(outDir + '/404.html'), + ); + await fs.promises.copyFile( + path.resolve(__dirname + '/../src/index.html'), + path.resolve(outDir + '/index.html'), + ); + await fs.promises.copyFile( + path.resolve(__dirname + '/../src/livecodes/html/app-base.html'), + path.resolve(outDir + '/app.html'), + ); +}; + +/** @type {Partial} */ +const baseOptions = { + bundle: true, + minify: devMode ? false : true, + outdir: 'build/livecodes', + format: 'esm', + target: 'es2020', + sourcemap: false, + sourcesContent: true, + define: { + ...getEnvVars(devMode), + }, + loader: { '.html': 'text', '.ttf': 'file' }, + logLevel: 'error', + external: ['@codemirror/*', '@lezer/*'], + plugins: [ + ...(devMode + ? [] + : [ + minifyHTML({ + collapseWhitespace: true, + collapseBooleanAttributes: true, + minifyJS: true, + minifyCSS: true, + processScripts: ['importmap'], + }), + ]), + ], +}; + +const sdkBuild = () => { + const sdkSrcDir = 'src/sdk/'; + const sdkSrcMod = sdkSrcDir + 'index.ts'; + const sdkOutDir = 'build/sdk/'; + + fs.copyFileSync(path.resolve('LICENSE'), path.resolve(sdkOutDir + 'LICENSE')); + fs.copyFileSync(path.resolve('README.md'), path.resolve(sdkOutDir + 'README.md')); + fs.copyFileSync( + path.resolve(sdkSrcDir + 'package.sdk.json'), + path.resolve(sdkOutDir + 'package.json'), + ); + + const sdkOptions = { + ...baseOptions, + target: 'es2018', + outdir: undefined, + }; + + return Promise.all([ + esbuild.build({ + ...sdkOptions, + entryPoints: [sdkSrcMod], + outdir: undefined, + outfile: sdkOutDir + 'livecodes.js', + }), + esbuild.build({ + ...sdkOptions, + entryPoints: [sdkSrcMod], + outdir: undefined, + outfile: sdkOutDir + 'livecodes.cjs', + format: 'cjs', + }), + esbuild.build({ + ...sdkOptions, + entryPoints: [sdkSrcMod], + outdir: undefined, + outfile: sdkOutDir + 'livecodes.umd.js', + format: 'iife', + globalName: 'livecodes', + }), + esbuild.build({ + ...sdkOptions, + entryPoints: [sdkSrcDir + 'react.tsx'], + outdir: undefined, + outfile: sdkOutDir + 'react.js', + external: ['react'], + jsx: 'automatic', + }), + esbuild.build({ + ...sdkOptions, + entryPoints: [sdkSrcDir + 'vue.ts'], + outdir: undefined, + outfile: sdkOutDir + 'vue.js', + external: ['vue'], + alias: { + '@vue/runtime-core': 'vue', + }, + }), + ]); +}; + +const esmBuild = () => + esbuild.build({ + ...baseOptions, + entryPoints: [ + 'app.ts', + 'embed.ts', + 'lite.ts', + 'headless.ts', + 'templates/starter/index.ts', + 'editor/monaco/monaco.ts', + 'editor/codemirror/codemirror.ts', + 'editor/codejar/codejar.ts', + 'editor/blockly/blockly.ts', + 'editor/quill/quill.ts', + 'import/import-src.ts', + 'services/firebase.ts', + 'services/google-fonts.ts', + 'languages/language-info.ts', + 'export/export.ts', + 'sync/sync.ts', + 'types/bundle-types.ts', + 'UI/open.ts', + 'UI/resources.ts', + 'UI/assets.ts', + 'UI/snippets.ts', + 'UI/backup.ts', + 'UI/broadcast.ts', + 'UI/import.ts', + 'UI/share.ts', + 'UI/deploy.ts', + 'UI/sync-ui.ts', + 'UI/embed-ui.ts', + 'UI/editor-settings.ts', + 'languages/diagrams/lang-diagrams-compiler-esm.ts', + 'languages/postgresql/lang-postgresql-compiler-esm.ts', + 'languages/r/lang-r-script-esm.ts', + 'languages/rescript/lang-rescript-compiler-esm.ts', + 'i18n/i18n.ts', + ] + .map((x) => 'src/livecodes/' + x) + .reduce(arrToObj, {}), + }); + +const iifeBuild = () => + esbuild.build({ + ...baseOptions, + format: 'iife', + entryPoints: [ + 'index.ts', + 'compiler/compile.page.ts', + 'compiler/compiler-utils.ts', + 'editor/custom-editor-utils.ts', + 'result/result-utils.ts', + 'languages/art-template/lang-art-template-compiler.ts', + 'languages/assemblyscript/lang-assemblyscript-script.ts', + 'languages/assemblyscript/lang-assemblyscript-compiler.ts', + 'languages/astro/lang-astro-compiler.ts', + 'languages/clio/lang-clio-compiler.ts', + 'languages/commonlisp/lang-commonlisp-script.ts', + 'languages/cpp/lang-cpp-script.ts', + 'languages/cpp-wasm/lang-cpp-wasm-script.ts', + 'languages/dot/lang-dot-compiler.ts', + 'languages/ejs/lang-ejs-compiler.ts', + 'languages/eta/lang-eta-compiler.ts', + 'languages/haml/lang-haml-compiler.ts', + 'languages/handlebars/lang-handlebars-compiler.ts', + 'languages/imba/lang-imba-compiler.ts', + 'languages/julia/lang-julia-script.ts', + 'languages/liquid/lang-liquid-compiler.ts', + 'languages/lua-wasm/lang-lua-wasm-script.ts', + 'languages/malina/lang-malina-compiler.ts', + 'languages/mustache/lang-mustache-compiler.ts', + 'languages/nunjucks/lang-nunjucks-compiler.ts', + 'languages/perl/lang-perl-script.ts', + 'languages/php-wasm/lang-php-wasm-script.ts', + 'languages/prolog/lang-prolog-script.ts', + 'languages/pug/lang-pug-compiler.ts', + 'languages/python-wasm/lang-python-wasm-script.ts', + 'languages/rescript/lang-rescript-formatter.ts', + 'languages/riot/lang-riot-compiler.ts', + 'languages/ruby-wasm/lang-ruby-wasm-script.ts', + 'languages/scss/lang-scss-compiler.ts', + 'languages/solid/lang-solid-compiler.ts', + 'languages/sql/lang-sql-compiler.ts', + 'languages/sql/lang-sql-script.ts', + 'languages/svelte/lang-svelte-compiler.ts', + 'languages/tcl/lang-tcl-script.ts', + 'languages/twig/lang-twig-compiler.ts', + 'languages/vento/lang-vento-compiler.ts', + 'languages/vue/lang-vue-compiler.ts', + 'languages/vue2/lang-vue2-compiler.ts', + 'languages/wat/lang-wat-compiler.ts', + 'languages/wat/lang-wat-script.ts', + 'languages/teal/lang-teal-compiler.ts', + 'languages/fennel/lang-fennel-compiler.ts', + 'languages/gleam/lang-gleam-compiler.ts', + 'languages/tailwindcss/processor-tailwindcss-compiler.ts', + 'languages/windicss/processor-windicss-compiler.ts', + 'languages/unocss/processor-unocss-compiler.ts', + 'languages/lightningcss/processor-lightningcss-compiler.ts', + 'languages/postcss/processor-postcss-compiler.ts', + ] + .map((x) => 'src/livecodes/' + x) + .reduce(arrToObj, {}), + }); + +/** @type {Partial} */ +const workerOptions = { + ...baseOptions, + entryPoints: [ + 'src/livecodes/compiler/compile.worker.ts', + 'src/livecodes/formatter/format.worker.ts', + 'src/livecodes/sync/sync.worker.ts', + ], + write: false, +}; + +const workersBuild = () => + esbuild.build(workerOptions).then((worker) => { + for (let out of worker.outputFiles || []) { + const content = uint8arrayToString(out.contents); + const filename = path.basename(out.path); + fs.writeFile( + path.resolve('build/livecodes', filename), + filename.endsWith('.map') ? content : iife(content), + () => { }, + ); + } + }); + +const functionsBuild = () => + esbuild.build({ + ...baseOptions, + outdir: 'functions/build', + entryPoints: ['src/livecodes/utils/compression.ts'], + }); + +const stylesBuild = () => buildStyles(devMode); + +prepareDir().then(async () => { + await buildLocalePathLoader(); + Promise.all([ + esmBuild(), + iifeBuild(), + workersBuild(), + stylesBuild(), + sdkBuild(), + buildI18n(), + ]).then(async () => { + if (!devMode) { + buildVendors(); + functionsBuild(); + } + await applyHash({ devMode }); + await injectCss(); + if (devMode) { + fs.writeFileSync( + path.resolve('build/tmp/trigger-reload.txt'), + new Date().toISOString(), + 'utf8', + ); + } + console.log('built to: ' + baseOptions.outdir + '/'); + }); +}); diff --git a/scripts/i18n-export.js b/scripts/i18n-export.js index 9483bed68..757d9a665 100644 --- a/scripts/i18n-export.js +++ b/scripts/i18n-export.js @@ -43,11 +43,11 @@ const sortedJSONify = (obj, space = 2) => (key, value) => value instanceof Object && !(value instanceof Array) ? Object.keys(value) - .sort() - .reduce((sorted, key) => { - sorted[key] = value[key]; - return sorted; - }, {}) + .sort() + .reduce((sorted, key) => { + sorted[key] = value[key]; + return sorted; + }, {}) : value, space, ); @@ -61,7 +61,7 @@ const writeTranslation = async (namespace, tmpMode) => { const name = namespace === 'translation' ? 'translation' : 'languageInfo'; const type = namespace === 'translation' ? 'I18nTranslation' : 'I18nLangInfoTranslation'; const code = `${autoGeneratedWarning} - + import type { I18nTranslationTemplate } from '../models'; // This is used as a template for other translations. @@ -70,9 +70,9 @@ const writeTranslation = async (namespace, tmpMode) => { // Since we allow nested objects, it is important to distinguish I18nTranslationTemplate from I18nAttributes. // In view of this, properties declared in I18nAttributes (and those attributes might be used in future) shall not be used as a nested key. - + const ${name} = ${sortedJSONify(trans[namespace])} as const satisfies I18nTranslationTemplate; - + export default ${name}; `; @@ -169,9 +169,9 @@ const abstractifyHTML = (html) => { node.attributes.length === 0 ? undefined : Array.from(node.attributes).reduce((acc, attr) => { - acc[attr.name] = attr.value; - return acc; - }, {}); + acc[attr.name] = attr.value; + return acc; + }, {}); elements.push({ name, attributes }); @@ -222,11 +222,12 @@ const generateElementsNote = (elements) => elements .map( (el, index) => - `### <${index + 1}> ###\n<${el.name} ${el.attributes - ? Object.keys(el.attributes) - .map((attr) => `${attr}="${el.attributes[attr]}"`) - .join(' ') - : '' + `### <${index + 1}> ###\n<${el.name} ${ + el.attributes + ? Object.keys(el.attributes) + .map((attr) => `${attr}="${el.attributes[attr]}"`) + .join(' ') + : '' } />\n\n`, ) .join(''); @@ -264,14 +265,14 @@ const processHTML = async (files) => { props.length === 1 ? getValueAndContext(element, props[0]) : props.reduce( - (acc, prop) => { - const vd = getValueAndContext(element, prop); - acc.value[prop] = vd.value; - acc.desc[prop] = vd.desc; - return acc; - }, - { value: {}, desc: {} }, - ); + (acc, prop) => { + const vd = getValueAndContext(element, prop); + acc.value[prop] = vd.value; + acc.desc[prop] = vd.desc; + return acc; + }, + { value: {}, desc: {} }, + ); addTranslation(key, value, desc, props); }); @@ -338,6 +339,19 @@ const processTS = async (files) => { ); }; +const walkSync = function (dir, fileList = []) { + const files = fs.readdirSync(dir); + files.forEach(function (file) { + const filePath = dir + path.sep + file; + if (fs.statSync(filePath).isDirectory()) { + fileList = walkSync(filePath, fileList); + } else { + fileList.push(filePath); + } + }); + return fileList; +}; + /** * Generate .ts and .lokalise.json files from .html and .ts files. * @@ -351,18 +365,22 @@ const generateTranslation = async () => { const TSFiles = []; if (!files.length) { - files.push(...fs.readdirSync(srcBaseDir, { recursive: true })); + files.push(...walkSync(srcBaseDir)); + // we can use this instead when we upgrade to node v20.1+ + // files.push(...fs.readdirSync(srcBaseDir, { recursive: true })); } HTMLFiles.push( ...files - .filter((file) => file.endsWith('.html') && file.startsWith(`html${path.sep}`)) + .filter( + (file) => + file.endsWith('.html') && file.startsWith(path.resolve(srcBaseDir, `html${path.sep}`)), + ) .map((file) => path.resolve(srcBaseDir, file)), ); TSFiles.push( ...files.filter((file) => file.endsWith('.ts')).map((file) => path.resolve(srcBaseDir, file)), ); - await processHTML(HTMLFiles); await processTS(TSFiles); diff --git a/scripts/i18n-import.mjs b/scripts/i18n-import.mjs index 8112494d6..43d7880f9 100644 --- a/scripts/i18n-import.mjs +++ b/scripts/i18n-import.mjs @@ -143,7 +143,7 @@ const importFromLokalise = async () => { continue; } - language = language.replace('_', '-'); + language = language.replace(/_/g, '-'); const outLanguagePath = path.join(outDir, language); console.log(`Importing language ${language}...`); @@ -164,11 +164,11 @@ const importFromLokalise = async () => { ); const sortedTranslationObject = sortedJSONify(translationObject); const code = `${autoGeneratedWarning} - + import type { ${type} } from '../models'; const ${name}: ${type} = ${sortedTranslationObject}; - + export default ${name}; `; diff --git a/scripts/styles.js b/scripts/styles.js index eae757749..1b22e894b 100644 --- a/scripts/styles.js +++ b/scripts/styles.js @@ -12,7 +12,8 @@ const buildStyles = async (devMode = false) => { const getFileNames = async (dir = srcDir) => (await fs.promises.readdir(dir)) .filter((name) => !fs.statSync(dir + name).isDirectory()) - .filter((name) => ['css', 'scss'].some((t) => name.endsWith('.' + t))); + .filter((name) => ['css', 'scss'].some((t) => name.endsWith('.' + t))) + .filter((name) => !name.startsWith('inc-')); const files = await getFileNames(srcDir); diff --git a/scripts/vscode-intellisense.js b/scripts/vscode-intellisense.js index 69835f754..42103bf98 100644 --- a/scripts/vscode-intellisense.js +++ b/scripts/vscode-intellisense.js @@ -1,69 +1,69 @@ -const fs = require('fs'); -const path = require('path'); - -/** - * Add new custom data attributes for HTML intellisense here. - * - * To add new translatable attributes, add the attribute name to `I18nAttributes` in `src/livecodes/i18n/locales/models.ts`. - */ -const customDataSchema = { - version: 1.1, - globalAttributes: [ - { - name: 'data-i18n', - description: 'The key of the translation for current element.', - }, - { - name: 'data-i18n-prop', - description: 'Attributes of the element that should be translated, separated by space.', - valueSet: 'i18nProps', - }, - { - name: 'data-hint', - description: 'The tooltip of the element.', - }, - ], - valueSets: [], -}; - -const generateHTMLIntellisense = async () => { - const generateI18nPropsValueSet = () => { - return new Promise((resolve, reject) => { - fs.readFile('src/livecodes/i18n/locales/models.ts', 'utf8', (err, data) => { - if (err) { - console.error(err); - reject(err); - } else { - const i18nProps = data - .match(/I18nAttributes.+?{([\s\S]*?)}/)[1] - .split('\n') - .map( - (line) => - line - .trim() - .replace(/['|;?]/g, '') - .split(':')[0], - ) - .filter((line) => line !== ''); - customDataSchema.valueSets.push({ - name: customDataSchema.globalAttributes[1].valueSet, - values: i18nProps.map((value) => ({ name: value })), - }); - resolve(); - } - }); - }); - }; - - await generateI18nPropsValueSet(); - - const schemaPath = path.resolve(__dirname, '../.vscode/html.html-data.json'); - fs.writeFileSync(schemaPath, JSON.stringify(customDataSchema, null, 2)); - console.log(`HTML Intellisense schema generated at ${schemaPath}`); -}; - -module.exports = { generateHTMLIntellisense }; - -if (require.main === module) { - generateHTMLIntellisense(); -} +const fs = require('fs'); +const path = require('path'); + +/** + * Add new custom data attributes for HTML intellisense here. + * + * To add new translatable attributes, add the attribute name to `I18nAttributes` in `src/livecodes/i18n/locales/models.ts`. + */ +const customDataSchema = { + version: 1.1, + globalAttributes: [ + { + name: 'data-i18n', + description: 'The key of the translation for current element.', + }, + { + name: 'data-i18n-prop', + description: 'Attributes of the element that should be translated, separated by space.', + valueSet: 'i18nProps', + }, + { + name: 'data-hint', + description: 'The tooltip of the element.', + }, + ], + valueSets: [], +}; + +const generateHTMLIntellisense = async () => { + const generateI18nPropsValueSet = () => { + return new Promise((resolve, reject) => { + fs.readFile('src/livecodes/i18n/locales/models.ts', 'utf8', (err, data) => { + if (err) { + console.error(err); + reject(err); + } else { + const i18nProps = data + .match(/I18nAttributes.+?{([\s\S]*?)}/)[1] + .split('\n') + .map( + (line) => + line + .trim() + .replace(/['|;?]/g, '') + .split(':')[0], + ) + .filter((line) => line !== ''); + customDataSchema.valueSets.push({ + name: customDataSchema.globalAttributes[1].valueSet, + values: i18nProps.map((value) => ({ name: value })), + }); + resolve(); + } + }); + }); + }; + + await generateI18nPropsValueSet(); + + const schemaPath = path.resolve(__dirname, '../.vscode/html.html-data.json'); + fs.writeFileSync(schemaPath, JSON.stringify(customDataSchema, null, 2)); + console.log(`HTML Intellisense schema generated at ${schemaPath}`); +}; + +module.exports = { generateHTMLIntellisense }; + +if (require.main === module) { + generateHTMLIntellisense(); +} diff --git a/src/404.html b/src/404.html index 489d8b097..562664a1b 100644 --- a/src/404.html +++ b/src/404.html @@ -47,7 +47,7 @@ overflow-x: hidden; } body { - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + font-family: system-ui, 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background-color: #f5f5f5; } #header { @@ -75,7 +75,7 @@ } iframe { border: 1px solid black; - border-radius: 5px; + border-radius: 8px; box-shadow: 0 0 20px #a4a6a8; height: 50vh; min-height: 350px; diff --git a/src/livecodes/UI/assets.ts b/src/livecodes/UI/assets.ts index ac874ee94..de1957094 100644 --- a/src/livecodes/UI/assets.ts +++ b/src/livecodes/UI/assets.ts @@ -8,6 +8,7 @@ import type { GitHubFile } from '../services/github'; import { generateId, type Storage } from '../storage'; import { addAssetScreen, assetsScreen } from '../html'; import { copyToClipboard, isMobile, loadScript } from '../utils/utils'; +import { iconDelete as deleteIcon } from '../UI/icons'; import { flexSearchUrl } from '../vendors'; import { getAddAssetButton, @@ -90,7 +91,7 @@ const createLinkContent = (item: Asset, baseUrl: string) => { detailsContainer.appendChild(date); const url = document.createElement('div'); - url.classList.add('light', 'overflow-text'); + url.classList.add('light', 'asset-url', 'overflow-text'); url.textContent = window.deps.translateString('assets.link.url', 'URL: {{url}}', { url: item.url, }); @@ -111,22 +112,23 @@ const createAssetItem = ( const link = document.createElement('a'); link.href = '#'; link.dataset.id = item.id; - link.classList.add('asset-link', 'hint--top'); - link.dataset.hint = window.deps.translateString( - 'assets.generic.clickToCopyURL', - 'Click to copy URL', - ); + link.classList.add('asset-link'); + link.title = window.deps.translateString('assets.generic.clickToCopyURL', 'Click to copy URL'); link.appendChild(createLinkContent(item, baseUrl)); link.onclick = (ev) => { ev.preventDefault(); copyUrl(item.url, notifications); }; - li.appendChild(link); - const deleteButton = document.createElement('button'); - deleteButton.classList.add('delete-button'); - li.appendChild(deleteButton); + const actions = document.createElement('div'); + actions.classList.add('actions'); + li.appendChild(actions); + const deleteButton = document.createElement('div'); + deleteButton.innerHTML = deleteIcon; + deleteButton.classList.add('action-button', 'delete-button'); + deleteButton.title = window.deps.translateString('assets.action.delete', 'Delete'); + actions.appendChild(deleteButton); return { link, deleteButton }; }; diff --git a/src/livecodes/UI/backup.ts b/src/livecodes/UI/backup.ts index 70be12a84..2fc4cc6a4 100644 --- a/src/livecodes/UI/backup.ts +++ b/src/livecodes/UI/backup.ts @@ -39,7 +39,7 @@ const createBackupContainer = (eventsManager: ReturnType getBackupLink()?.dataset.hint === inProgressMessage; +export const isInProgress = () => getBackupLink()?.title === inProgressMessage; export const updateProgressStatus = ({ inProgress, @@ -55,8 +55,7 @@ export const updateProgressStatus = ({ if (inProgress ?? isInProgress()) { if (backupLink) { - backupLink.classList.add('hint--bottom'); - backupLink.dataset.hint = inProgressMessage; + backupLink.title = inProgressMessage; } backupBtn.innerText = inProgressMessage; backupBtn.disabled = true; @@ -64,8 +63,7 @@ export const updateProgressStatus = ({ fileInput.disabled = true; } else { if (backupLink) { - backupLink.classList.remove('hint--bottom'); - backupLink.dataset.hint = ''; + backupLink.title = ''; } backupBtn.innerText = window.deps.translateString('backup.backupBtn', 'Backup'); backupBtn.disabled = false; diff --git a/src/livecodes/UI/editor-settings.ts b/src/livecodes/UI/editor-settings.ts index 11b5945a6..232af70bb 100644 --- a/src/livecodes/UI/editor-settings.ts +++ b/src/livecodes/UI/editor-settings.ts @@ -1,14 +1,7 @@ /* eslint-disable import/no-internal-modules */ import type { createEventsManager } from '../events'; import type { createModal } from '../modal'; -import type { - AppLanguage, - Config, - EditorLibrary, - EditorOptions, - FormatFn, - UserConfig, -} from '../models'; +import type { Config, EditorLibrary, EditorOptions, FormatFn, UserConfig } from '../models'; import type { createEditor } from '../editor/create-editor'; import { editorSettingsScreen } from '../html'; import { getEditorConfig, getFormatterConfig } from '../config/config'; @@ -25,14 +18,12 @@ export const createEditorSettingsUI = async ({ modal, eventsManager, scrollToSelector, - appLanguages, deps, }: { baseUrl: string; modal: ReturnType; eventsManager: ReturnType; scrollToSelector: string; - appLanguages: Record, string>; deps: { getUserConfig: () => UserConfig; createEditor: typeof createEditor; @@ -70,21 +61,6 @@ export const createEditorSettingsUI = async ({ note?: string; } const formFields: FormField[] = [ - { - title: window.deps.translateString('editorSettings.appLanguage.heading', 'App UI Language'), - name: 'appLanguage', - options: [ - ...Object.entries(appLanguages).map(([code, lng]) => ({ - label: lng, - value: code, - })), - ], - note: window.deps.translateString( - 'editorSettings.appLanguage.note', - 'Will reload the app to apply the changes after switching the language.', - ), - help: `${process.env.DOCS_BASE_URL}features/i18n`, - }, { title: window.deps.translateString( 'editorSettings.enableAI.heading', @@ -95,9 +71,10 @@ export const createEditorSettingsUI = async ({ help: `${process.env.DOCS_BASE_URL}features/ai`, note: window.deps.translateString( 'editorSettings.enableAI.note', - 'Powered by
Codeium', + 'Powered by Codeium', { isHTML: true, + baseUrl, }, ), }, @@ -341,7 +318,7 @@ export const createEditorSettingsUI = async ({ title = document.createElement('label'); title.innerHTML = field.title.replace( '*', - `*`, + `*`, ); title.dataset.name = field.name; form.appendChild(title); @@ -355,8 +332,8 @@ export const createEditorSettingsUI = async ({ helpLink.title = window.deps.translateString('generic.clickForInfo', 'Click for info...'); title?.appendChild(helpLink); - const helpIcon: HTMLImageElement = document.createElement('img'); - helpIcon.src = baseUrl + 'assets/icons/info.svg'; + const helpIcon: HTMLSpanElement = document.createElement('span'); + helpIcon.classList.add('icon-info'); helpLink.appendChild(helpIcon); } diff --git a/src/livecodes/UI/embed-ui.ts b/src/livecodes/UI/embed-ui.ts index 22c608973..d460d0a3b 100644 --- a/src/livecodes/UI/embed-ui.ts +++ b/src/livecodes/UI/embed-ui.ts @@ -9,7 +9,6 @@ import { cloneObject, copyToClipboard, encodeHTML, escapeCode, indentCode } from import { permanentUrlService } from '../services/permanent-url'; export const createEmbedUI = async ({ - baseUrl, config, editorLanguages, modal, @@ -18,7 +17,6 @@ export const createEmbedUI = async ({ createEditorFn, getUrlFn, }: { - baseUrl: string; config: ContentConfig; editorLanguages: { [key in EditorId]: string }; modal: ReturnType; @@ -51,10 +49,12 @@ export const createEmbedUI = async ({ | 'readonly' | 'mode' | 'view' + | 'layout' | 'activeEditor' - | 'permanentUrl' + | 'editor' | 'tools' - | 'activeTool'; + | 'activeTool' + | 'permanentUrl'; options: Array<{ label?: string; value: string; checked?: boolean }>; help?: string; } @@ -104,6 +104,10 @@ export const createEmbedUI = async ({ value: 'full', checked: true, }, + { + label: window.deps.translateString('embed.mode.simple', 'Simple'), + value: 'simple', + }, { label: window.deps.translateString('embed.mode.editor', 'Editor'), value: 'editor', @@ -139,6 +143,26 @@ export const createEmbedUI = async ({ ], help: `${process.env.DOCS_BASE_URL}features/default-view`, }, + { + title: window.deps.translateString('embed.layout.heading', 'Layout'), + name: 'layout', + options: [ + { + label: window.deps.translateString('embed.layout.responsive', 'Responsive'), + value: 'responsive', + checked: true, + }, + { + label: window.deps.translateString('embed.layout.horizontal', 'Horizontal'), + value: 'horizontal', + }, + { + label: window.deps.translateString('embed.layout.vertical', 'Vertical'), + value: 'vertical', + }, + ], + help: `${process.env.DOCS_BASE_URL}configuration/configuration-object#layout`, + }, { title: window.deps.translateString('embed.activeEditor.heading', 'Active Editor'), name: 'activeEditor', @@ -166,6 +190,30 @@ export const createEmbedUI = async ({ }, ], }, + { + title: window.deps.translateString('embed.codeEditor.heading', 'Code Editor'), + name: 'editor', + options: [ + { + label: window.deps.translateString('embed.codeEditor.default', 'Default'), + value: '', + checked: true, + }, + { + label: window.deps.translateString('embed.codeEditor.monaco', 'Monaco'), + value: 'monaco', + }, + { + label: window.deps.translateString('embed.codeEditor.codeMirror', 'CodeMirror'), + value: 'codemirror', + }, + { + label: window.deps.translateString('embed.codeEditor.codeJar', 'CodeJar'), + value: 'codejar', + }, + ], + help: `${process.env.DOCS_BASE_URL}features/editor-settings#code-editor`, + }, { title: window.deps.translateString('embed.tools.heading', 'Tools'), name: 'tools', @@ -242,8 +290,8 @@ export const createEmbedUI = async ({ helpLink.title = window.deps.translateString('generic.clickForInfo', 'Click for info...'); title.appendChild(helpLink); - const helpIcon: HTMLImageElement = document.createElement('img'); - helpIcon.src = baseUrl + 'assets/icons/info.svg'; + const helpIcon = document.createElement('i'); + helpIcon.classList.add('icon-info'); helpLink.appendChild(helpIcon); } @@ -301,7 +349,7 @@ export const createEmbedUI = async ({ const config = { ...(data.mode !== defaultConfig.mode ? { mode: data.mode } : {}), ...(data.theme !== defaultConfig.theme ? { theme: data.theme } : {}), - ...(data.tools !== 'closed' || data.activeTool !== 'console' + ...(!data.lite && (data.tools !== 'closed' || data.activeTool !== 'console') ? { tools: { enabled: data.tools === 'none' ? [] : 'all', @@ -314,6 +362,8 @@ export const createEmbedUI = async ({ ...(data.mode !== 'result' && data.activeEditor !== activeEditor ? { activeEditor: data.activeEditor } : {}), + ...(data.editor ? { editor: data.editor } : {}), + ...(data.layout !== defaultConfig.layout ? { layout: data.layout } : {}), }; const importId = urlObj.searchParams.get('x'); return { @@ -345,7 +395,7 @@ export const createEmbedUI = async ({ if (data.theme && data.theme !== defaultConfig.theme) { iframeUrl.searchParams.set('theme', String(data.theme)); } - if (data.tools && (data.tools !== 'closed' || data.activeTool !== 'console')) { + if (data.tools && !data.lite && (data.tools !== 'closed' || data.activeTool !== 'console')) { iframeUrl.searchParams.set( data.tools === 'none' ? 'tools' : String(data.activeTool), String(data.tools), @@ -354,6 +404,12 @@ export const createEmbedUI = async ({ if (data.readonly !== undefined) { iframeUrl.searchParams.set('readonly', String(data.readonly)); } + if (data.editor) { + iframeUrl.searchParams.set('editor', String(data.editor)); + } + if (data.layout && data.layout !== defaultConfig.layout) { + iframeUrl.searchParams.set('layout', String(data.layout)); + } return decodeURIComponent(iframeUrl.href); }; @@ -436,7 +492,7 @@ export default function App() { nonEmbeddedUrl.searchParams.delete('lite'); const projectUrl = decodeURIComponent(nonEmbeddedUrl.href); return ` - `.trimStart(); @@ -481,6 +537,7 @@ export default function App() { view: 'split', tools: 'closed', activeTool: 'console', + editor: '', }; const generateCode = async () => { @@ -520,16 +577,26 @@ export default function App() { const activeToolInputs = document.querySelectorAll( 'input[name="embed-activeTool"]', ); + const editorInputs = document.querySelectorAll('input[name="embed-editor"]'); if (formData.lite) { - previousSelections.tools = formData.tools || previousSelections.tools; + previousSelections.tools = formData.tools ?? previousSelections.tools; + previousSelections.editor = formData.editor ?? previousSelections.editor; delete formData.tools; + delete formData.editor; toolsInputs.forEach((input) => { input.checked = false; input.disabled = true; }); + editorInputs.forEach((input) => { + input.checked = false; + input.disabled = true; + if (input.value === 'codejar') { + input.checked = true; + } + }); } else { toolsInputs.forEach((input) => { - if (input.value === (formData.tools || previousSelections.tools)) { + if (input.value === (formData.tools ?? previousSelections.tools)) { input.checked = true; } input.disabled = false; @@ -537,6 +604,15 @@ export default function App() { formData.tools = input.value; } }); + editorInputs.forEach((input) => { + if (input.value === (formData.editor ?? previousSelections.editor)) { + input.checked = true; + } + input.disabled = false; + if (input.checked) { + formData.editor = input.value; + } + }); } if (formData.lite || formData.tools === 'none') { diff --git a/src/livecodes/UI/icons.ts b/src/livecodes/UI/icons.ts index 23ff80646..247dad0da 100644 --- a/src/livecodes/UI/icons.ts +++ b/src/livecodes/UI/icons.ts @@ -1,28 +1,13 @@ -export const run = - ''; +export const run = ''; -export const checked = - ''; +export const checked = ''; -export const unchecked = - ''; +export const unchecked = ''; -export const reset = - ''; +export const reset = ''; -export const edit = - ''; +export const edit = ''; -export const copy = ` - - - - - `; +export const copy = ''; + +export const iconDelete = ''; diff --git a/src/livecodes/UI/login.ts b/src/livecodes/UI/login.ts index f03171452..ffe8ff90e 100644 --- a/src/livecodes/UI/login.ts +++ b/src/livecodes/UI/login.ts @@ -61,29 +61,26 @@ export const displayLoggedIn = (user: User) => { loginLink.style.display = 'none'; } const logOutLink = getLogoutLink(); - if (logOutLink) { + const logOutText = logOutLink?.querySelector('span'); + if (logOutLink && logOutText) { const displayName = user.displayName || user.username; - logOutLink.innerHTML = window.deps.translateString('login.logout', 'Log out'); - logOutLink.classList.add('hint--bottom'); - logOutLink.dataset.hint = window.deps.translateString( - 'login.loginAs', - 'Logged in as {{name}}', - { - name: displayName!, - }, - ); - logOutLink.style.display = 'block'; + logOutText.innerHTML = window.deps.translateString('login.logout', 'Log out'); + logOutLink.title = window.deps.translateString('login.loginAs', 'Logged in as {{name}}', { + name: displayName!, + }); + logOutLink.style.display = 'flex'; } }; export const displayLoggedOut = () => { const loginLink = getLoginLink(); if (loginLink) { - loginLink.style.display = 'block'; + loginLink.style.display = 'flex'; } const logOutLink = getLogoutLink(); - if (logOutLink) { - logOutLink.innerHTML = window.deps.translateString('login.logout', 'Log out'); + const logOutText = logOutLink?.querySelector('span'); + if (logOutLink && logOutText) { + logOutText.innerHTML = window.deps.translateString('login.logout', 'Log out'); logOutLink.style.display = 'none'; } }; diff --git a/src/livecodes/UI/open.ts b/src/livecodes/UI/open.ts index 375454665..9f85db6fa 100644 --- a/src/livecodes/UI/open.ts +++ b/src/livecodes/UI/open.ts @@ -23,6 +23,11 @@ export const createOpenItem = ( link.href = '#'; link.dataset.id = item.id; link.classList.add('open-project-link'); + + const container = document.createElement('div'); + container.classList.add('open-project-item'); + link.appendChild(container); + const lastModified = isMobile() ? new Date(item.lastModified).toLocaleDateString() : new Date(item.lastModified).toLocaleString(); @@ -63,7 +68,7 @@ export const createOpenItem = ( const title = document.createElement('div'); title.classList.add('open-title', 'overflow-text'); title.textContent = item.title; - link.appendChild(title); + container.appendChild(title); const lastModifiedText = document.createElement('div'); lastModifiedText.classList.add('light'); @@ -74,18 +79,22 @@ export const createOpenItem = ( modified: lastModified, }, ); - link.appendChild(lastModifiedText); + container.appendChild(lastModifiedText); const tags = document.createElement('div'); tags.classList.add('project-tags'); langs.forEach((lang) => tags.append(lang)); tags.innerHTML += userTags.length > 0 ? ' | ' : ''; userTags.forEach((tag) => tags.append(tag)); - link.appendChild(tags); + container.appendChild(tags); const setAsDefault = document.createElement('div'); setAsDefault.classList.add('template-default'); + const iconTemplate = document.createElement('i'); + iconTemplate.classList.add('icon-file-template'); + setAsDefault.appendChild(iconTemplate); + const setAsDefaultLink = document.createElement('span'); setAsDefaultLink.innerText = window.deps.translateString('open.setAsDefault', 'Set as default'); setAsDefaultLink.classList.add('template-default-link'); @@ -101,7 +110,7 @@ export const createOpenItem = ( const removeDefaultLink = document.createElement('span'); removeDefaultLink.innerText = window.deps.translateString('open.removeDefault', '(unset)'); - removeDefaultLink.classList.add('template-remove-default-link'); + removeDefaultLink.classList.add('template-remove-default-link', 'delete'); defaultTemplateLabel.appendChild(removeDefaultLink); if (isTemplate) { @@ -109,9 +118,16 @@ export const createOpenItem = ( } li.appendChild(link); + const actions = document.createElement('div'); + actions.classList.add('actions'); + li.appendChild(actions); + const deleteButton = document.createElement('button'); deleteButton.classList.add('delete-button'); - li.appendChild(deleteButton); + const iconCSS = ''; + deleteButton.title = window.deps.translateString('open.action.delete', 'Delete'); + deleteButton.innerHTML = `${iconCSS}`; + actions.appendChild(deleteButton); return { link, deleteButton, setAsDefaultLink, removeDefaultLink }; }; diff --git a/src/livecodes/UI/resources.ts b/src/livecodes/UI/resources.ts index 8e50a04b9..18fd1b2f6 100644 --- a/src/livecodes/UI/resources.ts +++ b/src/livecodes/UI/resources.ts @@ -136,7 +136,7 @@ export const createExternalResourcesUI = ({ itemScriptLink.onclick = scriptAdded ? null : () => addResource(files.js || '', 'scripts'); itemScriptLink.title = files.js; itemScriptLink.dataset.resourceUrl = files.js; - itemScriptLink.innerHTML = ``; + itemScriptLink.innerHTML = ` JS`; itemScript.appendChild(itemScriptLink); } @@ -155,7 +155,7 @@ export const createExternalResourcesUI = ({ : () => addResource(files.css || '', 'stylesheets'); itemStylesheetLink.title = files.css; itemStylesheetLink.dataset.resourceUrl = files.css; - itemStylesheetLink.innerHTML = ``; + itemStylesheetLink.innerHTML = ` CSS`; itemStylesheet.appendChild(itemStylesheetLink); } diff --git a/src/livecodes/UI/selectors.ts b/src/livecodes/UI/selectors.ts index df86ca059..e1312dac7 100644 --- a/src/livecodes/UI/selectors.ts +++ b/src/livecodes/UI/selectors.ts @@ -1,75 +1,112 @@ export const getToolbarElement = /* @__PURE__ */ () => document.querySelector('#toolbar') as HTMLElement; + export const getProjectTitleElement = /* @__PURE__ */ () => document.querySelector('#project-title') as HTMLElement; + export const getEditorContainerElement = /* @__PURE__ */ () => document.querySelector('#editor-container') as HTMLElement; + export const getEditorsElement = /* @__PURE__ */ () => document.querySelector('#editors') as HTMLElement; + export const getMarkupElement = /* @__PURE__ */ () => document.querySelector('#markup') as HTMLElement; + export const getStyleElement = /* @__PURE__ */ () => document.querySelector('#style') as HTMLElement; + export const getScriptElement = /* @__PURE__ */ () => document.querySelector('#script') as HTMLElement; + export const getOutputElement = /* @__PURE__ */ () => document.querySelector('#output') as HTMLElement; + export const getResultElement = /* @__PURE__ */ () => document.querySelector('#result') as HTMLElement; + export const getResultIFrameElement = /* @__PURE__ */ () => document.querySelector('#result > iframe') as HTMLIFrameElement; export const getGutterElement = /* @__PURE__ */ () => document.querySelector('#editor-container .gutter') as HTMLElement; + export const getLogoLink = /* @__PURE__ */ () => - document.querySelector('#logo a') as HTMLAnchorElement; + document.querySelector('a#logo') as HTMLAnchorElement; export const getRunButton = /* @__PURE__ */ () => document.querySelector('#run-button') as HTMLElement; -export const getCodeRunButton = /* @__PURE__ */ () => - document.querySelector('#code-run-button') as HTMLElement; + +export const getLightThemeButton = /* @__PURE__ */ () => + document.querySelector('#light-theme-button') as HTMLElement; + +export const getDarkThemeButton = /* @__PURE__ */ () => + document.querySelector('#dark-theme-button') as HTMLElement; + +export const getI18nMenuContainer = /* @__PURE__ */ () => + document.querySelector('#app-menu-container-i18n') as HTMLElement; + export const getEditorToolbar = /* @__PURE__ */ () => document.querySelector('#editor-tools') as HTMLElement; + export const getFocusButton = /* @__PURE__ */ () => document.querySelector('#editor-tools #focus-btn') as HTMLElement; + export const getCopyButton = /* @__PURE__ */ () => document.querySelector('#editor-tools #copy-btn') as HTMLElement; + export const getCopyAsUrlButton = /* @__PURE__ */ () => document.querySelector('#editor-tools #copy-as-url-btn') as HTMLElement; + export const getUndoButton = /* @__PURE__ */ () => document.querySelector('#editor-tools #undo-btn') as HTMLElement; + export const getRedoButton = /* @__PURE__ */ () => document.querySelector('#editor-tools #redo-btn') as HTMLElement; + export const getFormatButton = /* @__PURE__ */ () => document.querySelector('#editor-tools #format-btn') as HTMLElement; + export const getEditorModeNode = /* @__PURE__ */ () => document.querySelector('#editor-mode'); export const getEditorStatus = /* @__PURE__ */ () => document.querySelector('#editor-tools #editor-status') as HTMLElement; + export const getExternalResourcesBtn = /* @__PURE__ */ () => document.querySelector('#editor-tools #external-resources-btn') as HTMLElement; + export const getExternalResourcesMark = /* @__PURE__ */ () => document.querySelector('#editor-tools #external-resources-mark') as HTMLElement; + export const getProjectInfoBtn = /* @__PURE__ */ () => document.querySelector('#editor-tools #project-info-btn') as HTMLElement; + export const getCustomSettingsBtn = /* @__PURE__ */ () => document.querySelector('#editor-tools #custom-settings-btn') as HTMLElement; + export const getEditorSettingsBtn = /* @__PURE__ */ () => document.querySelector('#editor-tools #editor-settings-btn') as HTMLElement; + export const getShareButton = /* @__PURE__ */ () => document.querySelector('#share-button') as HTMLElement; + export const getResultButton = /* @__PURE__ */ () => document.querySelector('#result-button') as HTMLElement; + export const getFullscreenButton = /* @__PURE__ */ () => document.querySelector('#fullscreen-button') as HTMLElement; + export const getEditorTitles = /* @__PURE__ */ () => document.querySelectorAll('.editor-title:not(.hidden)'); export const getEditorDivs = /* @__PURE__ */ () => document.querySelectorAll('#editors > .editor'); export const getToolspaneElement = /* @__PURE__ */ () => document.querySelector('#output #tools-pane') as HTMLElement; + export const getToolspaneBar = /* @__PURE__ */ () => document.querySelector('#output #tools-pane-bar') as HTMLElement; + export const getToolspaneButtons = /* @__PURE__ */ () => document.querySelector('#tools-pane-buttons') as HTMLElement; + export const getToolspaneTitles = /* @__PURE__ */ () => document.querySelector('#tools-pane-titles'); export const getToolspaneLoader = /* @__PURE__ */ () => @@ -78,21 +115,28 @@ export const getZoomButtonValue = /* @__PURE__ */ () => document.querySelector('#zoom-button #zoom-value'); export const getModalSaveButton = /* @__PURE__ */ () => document.querySelector('#modal #prompt-save-btn') as HTMLElement; + export const getModalDoNotSaveButton = /* @__PURE__ */ () => document.querySelector('#modal #prompt-donot-save-btn') as HTMLElement; + export const getModalCancelButton = /* @__PURE__ */ () => document.querySelector('#modal #prompt-cancel-btn') as HTMLElement; export const getModalRecoverButton = /* @__PURE__ */ () => document.querySelector('#modal #prompt-recover-btn') as HTMLElement; + export const getModalSavePreviousButton = /* @__PURE__ */ () => document.querySelector('#modal #prompt-save-previous-btn') as HTMLElement; + export const getModalCancelRecoverButton = /* @__PURE__ */ () => document.querySelector('#modal #prompt-cancel-recover-btn') as HTMLElement; + export const getModalUnsavedName = /* @__PURE__ */ () => document.querySelector('#modal #unsaved-project-name') as HTMLElement; + export const getModalUnsavedLastModified = /* @__PURE__ */ () => document.querySelector('#modal #unsaved-project-last-modified') as HTMLElement; + export const getModalDisableRecoverCheckbox = /* @__PURE__ */ () => document.querySelector('#modal #disable-recover-checkbox') as HTMLInputElement; @@ -106,15 +150,28 @@ export const getstyleMenu = /* @__PURE__ */ () => document.querySelector('#style-selector .dropdown-menu'); export const getSettingToggles = /* @__PURE__ */ () => - document.querySelectorAll('#settings-menu input'); + document.querySelectorAll('#app-menu-settings input'); + +export const getThemeColorSelector = /* @__PURE__ */ () => + document.querySelector('#app-menu-settings #theme-color-selector'); export const getCssPresetLinks = /* @__PURE__ */ () => document.querySelectorAll('#css-preset-menu a'); -export const getSettingsMenuScroller = /* @__PURE__ */ () => - document.querySelector('#settings-menu-container'); -export const getSettingsButton = /* @__PURE__ */ () => - document.querySelector('#settings-button'); +export const getAppMenuProjectScroller = /* @__PURE__ */ () => + document.querySelector('#app-menu-container-project'); +export const getAppMenuProjectButton = /* @__PURE__ */ () => + document.querySelector('#app-menu-button-project'); + +export const getAppMenuSettingsScroller = /* @__PURE__ */ () => + document.querySelector('#app-menu-container-settings'); +export const getAppMenuSettingsButton = /* @__PURE__ */ () => + document.querySelector('#app-menu-button-settings'); + +export const getAppMenuHelpScroller = /* @__PURE__ */ () => + document.querySelector('#app-menu-container-help'); +export const getAppMenuHelpButton = /* @__PURE__ */ () => + document.querySelector('#app-menu-button-help'); export const getExportJSONLink = /* @__PURE__ */ () => document.querySelector('#export-menu #export-json'); @@ -195,55 +252,55 @@ export const getAboutLink = /* @__PURE__ */ () => document.querySelector('#about-link'); export const getAutoupdateToggle = /* @__PURE__ */ () => - document.querySelector('#settings-menu input#autoupdate') as HTMLInputElement; + document.querySelector('#app-menu-settings input#autoupdate') as HTMLInputElement; export const getDelayValue = /* @__PURE__ */ () => - document.querySelector('#settings-menu #delay-value') as HTMLElement; + document.querySelector('#app-menu-settings #delay-value') as HTMLElement; export const getDelayRange = /* @__PURE__ */ () => - document.querySelector('#settings-menu input#delay-range') as HTMLInputElement; + document.querySelector('#app-menu-settings input#delay-range') as HTMLInputElement; export const getAutosaveToggle = /* @__PURE__ */ () => - document.querySelector('#settings-menu input#autosave') as HTMLInputElement; + document.querySelector('#app-menu-settings input#autosave') as HTMLInputElement; export const getAutosyncToggle = /* @__PURE__ */ () => - document.querySelector('#settings-menu input#autosync') as HTMLInputElement; + document.querySelector('#app-menu-settings input#autosync') as HTMLInputElement; export const getFormatOnsaveToggle = /* @__PURE__ */ () => - document.querySelector('#settings-menu input#formatOnsave') as HTMLInputElement; + document.querySelector('#app-menu-settings input#formatOnsave') as HTMLInputElement; export const getProcessorToggles = /* @__PURE__ */ () => document.querySelectorAll('#style-selector input'); export const getEmmetToggle = /* @__PURE__ */ () => - document.querySelector('#settings-menu input#emmet') as HTMLInputElement; + document.querySelector('#app-menu-settings input#emmet') as HTMLInputElement; export const getThemeToggle = /* @__PURE__ */ () => - document.querySelector('#settings-menu input#theme') as HTMLInputElement; + document.querySelector('#app-menu-settings input#theme') as HTMLInputElement; export const getLayoutToggle = /* @__PURE__ */ () => - document.querySelector('#settings-menu input#layout') as HTMLInputElement; + document.querySelector('#app-menu-settings input#layout') as HTMLInputElement; export const getShowWelcomeToggle = /* @__PURE__ */ () => - document.querySelector('#settings-menu input#welcome') as HTMLInputElement; + document.querySelector('#app-menu-settings input#welcome') as HTMLInputElement; export const getRecoverToggle = /* @__PURE__ */ () => - document.querySelector('#settings-menu input#recover-unsaved') as HTMLInputElement; + document.querySelector('#app-menu-settings input#recover-unsaved') as HTMLInputElement; export const getSpacingToggle = /* @__PURE__ */ () => - document.querySelector('#settings-menu input#show-spacing') as HTMLInputElement; + document.querySelector('#app-menu-settings input#show-spacing') as HTMLInputElement; export const getCSSPresetLinks = /* @__PURE__ */ () => document.querySelectorAll('#css-preset-menu a'); export const getProjectInfoLink = /* @__PURE__ */ () => - document.querySelector('#settings-menu #info-link') as HTMLInputElement; + document.querySelector('#app-menu-project #info-link') as HTMLInputElement; export const getAssetsLink = /* @__PURE__ */ () => - document.querySelector('#settings-menu #assets-link') as HTMLInputElement; + document.querySelector('#app-menu-settings #assets-link') as HTMLInputElement; export const getSnippetsLink = /* @__PURE__ */ () => - document.querySelector('#settings-menu #snippets-link') as HTMLInputElement; + document.querySelector('#app-menu-settings #snippets-link') as HTMLInputElement; export const getInfoTitleInput = /* @__PURE__ */ () => document.querySelector('#info-container input#title-input') as HTMLInputElement; @@ -329,6 +386,7 @@ export const getNewRepoNameInput = /* @__PURE__ */ (deployContainer: HTMLElement deployContainer.querySelector('#new-repo-name') as HTMLInputElement; export const getNewRepoNameError = /* @__PURE__ */ (deployContainer: HTMLElement) => deployContainer.querySelector('#new-repo-name-error') as HTMLElement; + export const getNewRepoMessageInput = /* @__PURE__ */ (deployContainer: HTMLElement) => deployContainer.querySelector('#new-repo-message') as HTMLInputElement; export const getNewRepoCommitSource = /* @__PURE__ */ (deployContainer: HTMLElement) => @@ -488,14 +546,14 @@ export const getWelcomeLinkTemplates = /* @__PURE__ */ (welcomeContainer: HTMLEl welcomeContainer.querySelector('#welcome-link-templates') as HTMLAnchorElement; export const getModalShowWelcomeCheckbox = /* @__PURE__ */ (welcomeContainer: HTMLElement) => - welcomeContainer.querySelector('#modal #show-welcome-checkbox') as HTMLInputElement; + welcomeContainer.querySelector('#modal #welcome-show-checkbox') as HTMLInputElement; export const getModalWelcomeRecover = /* @__PURE__ */ ( welcomeContainer = /* @__PURE__ */ document, ) => welcomeContainer.querySelector('#modal #welcome-recover') as HTMLElement; export const getModalWelcomeScreen = /* @__PURE__ */ (welcomeContainer: HTMLElement) => - welcomeContainer.querySelector('#welcome-screen-container .modal-screen') as HTMLElement; + welcomeContainer.querySelector('#modal #welcome-screen-container') as HTMLElement; export const getModalWelcomeRecent = /* @__PURE__ */ (welcomeContainer: HTMLElement) => welcomeContainer.querySelector('#modal #welcome-recent') as HTMLElement; @@ -505,3 +563,6 @@ export const getModalWelcomeRecentList = /* @__PURE__ */ (welcomeContainer: HTML export const getModalWelcomeTemplateList = /* @__PURE__ */ (welcomeContainer: HTMLElement) => welcomeContainer.querySelector('#modal #welcome-template-list') as HTMLElement; + +export const getResultModeDrawer = /* @__PURE__ */ () => + document.querySelector('#result-mode-drawer') as HTMLElement; diff --git a/src/livecodes/UI/share.ts b/src/livecodes/UI/share.ts index 34631da78..e6cf30115 100644 --- a/src/livecodes/UI/share.ts +++ b/src/livecodes/UI/share.ts @@ -3,7 +3,7 @@ import type { createEventsManager } from '../events'; import { shareScreen } from '../html'; import type { ShareData } from '../models'; import { allowedOrigin } from '../services/allowed-origin'; -import { copyToClipboard, getAbsoluteUrl } from '../utils/utils'; +import { copyToClipboard } from '../utils/utils'; import { generateQrCode } from './qrcode'; import { getQrCodeContainer } from './selectors'; @@ -69,11 +69,10 @@ export const createShareContainer = async ( link.rel = 'noopener noreferrer'; link.innerHTML = ` ${service.name} `; @@ -104,30 +103,30 @@ export const createShareContainer = async ( const services: Service[] = [ { name: window.deps.translateString('share.services.facebook', 'Facebook'), - icon: 'facebook.svg', + icon: 'icon-share-facebook', createShareUrl: ({ url }) => `https://www.facebook.com/sharer.php?u=${encode(url)}`, }, { name: window.deps.translateString('share.services.twitter', '𝕏 / Twitter'), - icon: 'x.svg', + icon: 'icon-share-x', createShareUrl: ({ url, title }) => `https://twitter.com/intent/tweet?url=${encode(url)}&text=${encode(title)}`, }, { name: window.deps.translateString('share.services.hackerNews', 'Hacker News'), - icon: 'hacker-news.svg', + icon: 'icon-share-hacker', createShareUrl: ({ url, title }) => `https://news.ycombinator.com/submitlink?u=${encode(url)}&t=${encode(title)}`, }, { name: window.deps.translateString('share.services.reddit', 'Reddit'), - icon: 'reddit.svg', + icon: 'icon-share-reddit', createShareUrl: ({ url, title }) => `https://www.reddit.com/submit?url=${encode(url)}&title=${encode(title)}`, }, { name: window.deps.translateString('share.services.linkedIn', 'LinkedIn'), - icon: 'linkedin.svg', + icon: 'icon-share-linkedin', createShareUrl: ({ url, title }) => `https://www.linkedin.com/shareArticle?url=${encode(url)}&title=${encode( title, @@ -135,7 +134,7 @@ export const createShareContainer = async ( }, { name: window.deps.translateString('share.services.devTo', 'Dev.to'), - icon: 'dev.svg', + icon: 'icon-share-dev', createShareUrl: ({ url, title }) => `https://dev.to/new?prefill=${encode( '---\ntitle: ' + title + '\npublished: true\ntags: livecodes\n---\n\n\n\n' + url, @@ -143,13 +142,13 @@ export const createShareContainer = async ( }, { name: window.deps.translateString('share.services.tumblr', 'Tumblr'), - icon: 'tumblr.svg', + icon: 'icon-share-tumblr', createShareUrl: ({ url, title }) => `https://www.tumblr.com/share/link?url=${encode(url)}&name=${encode(title)}`, }, { name: window.deps.translateString('share.services.pinterest', 'Pinterest'), - icon: 'pinterest.svg', + icon: 'icon-share-pinterest', createShareUrl: ({ url, title }) => `https://pinterest.com/pin/create/bookmarklet/?url=${encode(url)}&description=${encode( title, @@ -157,40 +156,35 @@ export const createShareContainer = async ( }, { name: window.deps.translateString('share.services.whatsApp', 'WhatsApp'), - icon: 'whatsapp.svg', + icon: 'icon-share-whatsapp', createShareUrl: ({ url, title }) => `https://api.whatsapp.com/send?text=${encode(title)} ${encode(url)}`, }, { name: window.deps.translateString('share.services.telegram', 'Telegram'), - icon: 'telegram.svg', + icon: 'icon-share-telegram', createShareUrl: ({ url, title }) => `https://t.me/share/url?url=${encode(url)}&text=${encode(title)}`, }, { name: window.deps.translateString('share.services.pocket', 'Pocket'), - icon: 'pocket.svg', + icon: 'icon-share-pocket', createShareUrl: ({ url, title }) => `https://getpocket.com/save?url=${encode(url)}&title=${encode(title)}`, }, { name: window.deps.translateString('share.services.email', 'Email'), - icon: 'email.svg', + icon: 'icon-share-email', createShareUrl: ({ url, title }) => `mailto:?subject=${encode(title)}&body=${encode(url)}`, }, - { - name: window.deps.translateString('share.services.copyUrl', 'Copy URL'), - icon: 'copy.svg', - onClick: ({ url }) => copyUrl(url), - }, { name: window.deps.translateString('share.services.qrCode', 'QR code'), - icon: 'qr-code.svg', + icon: 'icon-share-qr', onClick: showQrCode, }, { name: window.deps.translateString('share.services.share', 'Share via …'), - icon: 'share.svg', + icon: 'icon-share', onClick: ({ url, title }) => navigator.share({ url, title }), }, ]; diff --git a/src/livecodes/UI/snippets.ts b/src/livecodes/UI/snippets.ts index 7f4b14fb1..ab7a1e9e9 100644 --- a/src/livecodes/UI/snippets.ts +++ b/src/livecodes/UI/snippets.ts @@ -8,7 +8,7 @@ import { addSnippetScreen, snippetsScreen } from '../html'; import { getLanguageTitle, languages } from '../languages'; import { copyToClipboard, isMobile, loadScript } from '../utils'; import { flexSearchUrl } from '../vendors'; -import { edit as editIcon, copy as copyIcon } from '../UI/icons'; +import { edit as editIcon, copy as copyIcon, iconDelete as deleteIcon } from '../UI/icons'; import { getAddSnippetButton, @@ -54,17 +54,21 @@ const createSnippetItem = ( const link = document.createElement('a'); link.href = '#'; - link.dataset.hint = window.deps.translateString( + link.title = window.deps.translateString( 'snippets.copy.clickToCopySnippet', 'Click to copy snippet', ); - link.classList.add('snippet-item', 'hint--top'); + link.classList.add('snippet-link'); link.title = item.description; link.onclick = (ev) => { ev.preventDefault(); copySnippet(item.code, notifications); }; + const container = document.createElement('div'); + container.classList.add('snippet-item'); + link.appendChild(container); + const lastModified = isMobile() ? new Date(item.lastModified).toLocaleDateString() : new Date(item.lastModified).toLocaleString(); @@ -72,7 +76,7 @@ const createSnippetItem = ( const title = document.createElement('div'); title.classList.add('open-title', 'overflow-text'); title.textContent = item.title; - link.appendChild(title); + container.appendChild(title); if (!isMobile()) { const lastModifiedText = document.createElement('div'); @@ -84,7 +88,7 @@ const createSnippetItem = ( modified: lastModified, }, ); - link.appendChild(lastModifiedText); + container.appendChild(lastModifiedText); } const tags = document.createElement('div'); @@ -95,11 +99,11 @@ const createSnippetItem = ( langEl.title = window.deps.translateString('snippets.filter.language', 'filter by language'); langEl.textContent = getLanguage(item.language); tags.append(langEl); - link.appendChild(tags); + container.appendChild(tags); const editorContainer = document.createElement('div'); editorContainer.classList.add('editor', 'custom-editor'); - link.appendChild(editorContainer); + container.appendChild(editorContainer); li.appendChild(link); const actions = document.createElement('div'); @@ -108,8 +112,8 @@ const createSnippetItem = ( const copyButton = document.createElement('div'); copyButton.innerHTML = copyIcon; - copyButton.classList.add('action-button', 'hint--left'); - copyButton.dataset.hint = window.deps.translateString('snippets.action.copy', 'Copy'); + copyButton.classList.add('action-button'); + copyButton.title = window.deps.translateString('snippets.action.copy', 'Copy'); copyButton.onclick = (ev) => { ev.preventDefault(); copySnippet(item.code, notifications); @@ -118,21 +122,18 @@ const createSnippetItem = ( const editButton = document.createElement('div'); editButton.innerHTML = editIcon; - editButton.classList.add('action-button', 'hint--left'); - editButton.dataset.hint = window.deps.translateString('snippets.action.edit', 'Edit'); + editButton.classList.add('action-button'); + editButton.title = window.deps.translateString('snippets.action.edit', 'Edit'); editButton.onclick = () => { showScreen('add-snippet', item.id); }; actions.appendChild(editButton); - const deleteWrapper = document.createElement('div'); - deleteWrapper.dataset.hint = window.deps.translateString('snippets.action.delete', 'Delete'); - deleteWrapper.classList.add('hint--left'); - actions.appendChild(deleteWrapper); - - const deleteButton = document.createElement('button'); - deleteButton.classList.add('delete-button'); - deleteWrapper.appendChild(deleteButton); + const deleteButton = document.createElement('div'); + deleteButton.innerHTML = deleteIcon; + deleteButton.classList.add('action-button', 'delete-button'); + deleteButton.title = window.deps.translateString('snippets.action.delete', 'Delete'); + actions.appendChild(deleteButton); return { link, deleteButton }; }; diff --git a/src/livecodes/UI/sync-ui.ts b/src/livecodes/UI/sync-ui.ts index 9f6392689..fcc1c1acc 100644 --- a/src/livecodes/UI/sync-ui.ts +++ b/src/livecodes/UI/sync-ui.ts @@ -57,7 +57,7 @@ const syncInProgressMessage = window.deps.translateString( 'sync.syncInProgress', 'Sync in progress...', ); -export const isSyncInProgress = () => getSyncLink()?.dataset.hint === syncInProgressMessage; +export const isSyncInProgress = () => getSyncLink()?.title === syncInProgressMessage; export const updateSyncStatus = ({ inProgress, @@ -80,8 +80,7 @@ export const updateSyncStatus = ({ if (inProgress ?? isSyncInProgress()) { if (syncLink) { - syncLink.classList.add('hint--bottom'); - syncLink.dataset.hint = syncInProgressMessage; + syncLink.title = syncInProgressMessage; syncIndicator?.classList.remove('hidden'); } startSyncBtns?.forEach((btn) => { @@ -90,8 +89,7 @@ export const updateSyncStatus = ({ }); } else { if (syncLink) { - syncLink.classList.toggle('hint--bottom', Boolean(lastSyncMessage)); - syncLink.dataset.hint = lastSyncMessage; + syncLink.title = lastSyncMessage; syncIndicator?.classList.add('hidden'); } startSyncBtns?.forEach((btn) => { diff --git a/src/livecodes/UI/templates.ts b/src/livecodes/UI/templates.ts index 834f9bb3d..407c940cf 100644 --- a/src/livecodes/UI/templates.ts +++ b/src/livecodes/UI/templates.ts @@ -48,8 +48,8 @@ export const createStarterTemplateLink = ( export const noUserTemplates = () => `