From a317a727c80ed9dd1eb55d9cef95d794822e2a93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaros=C5=82aw=20Sadzi=C5=84ski?= Date: Tue, 28 Mar 2023 18:11:40 +0200 Subject: [PATCH] (core) Adding tutorial card Summary: For now only html stub and docList adjustement for showing a tutorial card. It will be used in future diffs after tutorial implementation. Test Plan: Manual Reviewers: georgegevoian Reviewed By: georgegevoian Differential Revision: https://phab.getgrist.com/D3750 --- app/client/components/GristDoc.ts | 6 - app/client/models/AppModel.ts | 20 ++- app/client/models/UserPrefs.ts | 14 ++ app/client/ui/DocMenu.ts | 10 +- app/client/ui/DocMenuCss.ts | 15 +- app/client/ui/HomeLeftPane.ts | 13 +- app/client/ui/RightPanel.ts | 2 +- app/client/ui/TutorialCard.ts | 219 ++++++++++++++++++++++++++++++ app/client/ui2018/IconList.ts | 8 ++ app/common/Prefs.ts | 5 +- app/common/gristUrls.ts | 2 + static/icons/icons.css | 4 + static/locales/en.client.json | 2 +- static/ui-icons/UI/Board.svg | 1 + static/ui-icons/UI/Bookmark.svg | 1 + static/ui-icons/UI/GreenArrow.svg | 3 + static/ui-icons/UI/RemoveBig.svg | 26 ++++ test/nbrowser/DocTutorial.ts | 41 ++++++ test/nbrowser/HomeIntro.ts | 2 +- test/nbrowser/gristUtils.ts | 60 ++++++-- 20 files changed, 424 insertions(+), 30 deletions(-) create mode 100644 app/client/ui/TutorialCard.ts create mode 100644 static/ui-icons/UI/Board.svg create mode 100644 static/ui-icons/UI/Bookmark.svg create mode 100644 static/ui-icons/UI/GreenArrow.svg create mode 100644 static/ui-icons/UI/RemoveBig.svg diff --git a/app/client/components/GristDoc.ts b/app/client/components/GristDoc.ts index ceb7e243..3527ae6e 100644 --- a/app/client/components/GristDoc.ts +++ b/app/client/components/GristDoc.ts @@ -66,7 +66,6 @@ import {isList, isListType, isRefListType, RecalcWhen} from 'app/common/gristTyp import {HashLink, IDocPage, isViewDocPage, SpecialDocPage, ViewDocPage} from 'app/common/gristUrls'; import {undef, waitObs} from 'app/common/gutil'; import {LocalPlugin} from "app/common/plugin"; -import {DismissedPopup} from 'app/common/Prefs'; import {StringUnion} from 'app/common/StringUnion'; import {TableData} from 'app/common/TableData'; import {DocStateComparison} from 'app/common/UserAPI'; @@ -490,11 +489,6 @@ export class GristDoc extends DisposableWithEvents { this.draftMonitor = Drafts.create(this, this); this.cursorMonitor = CursorMonitor.create(this, this); this.editorMonitor = EditorMonitor.create(this, this); - - - G.window.resetSeenPopups = (seen = false) => { - this.docPageModel.appModel.dismissedPopups.set(seen ? DismissedPopup.values : []); - }; } /** diff --git a/app/client/models/AppModel.ts b/app/client/models/AppModel.ts index c8b75344..0841dc93 100644 --- a/app/client/models/AppModel.ts +++ b/app/client/models/AppModel.ts @@ -21,7 +21,7 @@ import {getDefaultThemePrefs, Theme, ThemeAppearance, ThemeColors, ThemePrefs, import {getThemeColors} from 'app/common/Themes'; import {getGristConfig} from 'app/common/urlUtils'; import {getOrgName, Organization, OrgError, UserAPI, UserAPIImpl} from 'app/common/UserAPI'; -import {getUserPrefObs, getUserPrefsObs} from 'app/client/models/UserPrefs'; +import {getUserPrefObs, getUserPrefsObs, markAsSeen, markAsUnSeen} from 'app/client/models/UserPrefs'; import {bundleChanges, Computed, Disposable, Observable, subscribe} from 'grainjs'; const t = makeT('AppModel'); @@ -111,6 +111,8 @@ export interface AppModel { isSupport(): boolean; // If user is a Support user isOwner(): boolean; // If user is an owner of this org isOwnerOrEditor(): boolean; // If user is an owner or editor of this org + /** Creates an computed observable to dismiss a popup or check if it was dismissed */ + dismissedPopup(name: DismissedPopup): Observable; } export class TopAppModelImpl extends Disposable implements TopAppModel { @@ -277,6 +279,10 @@ export class AppModelImpl extends Disposable implements AppModel { urlState().pushUrl({createTeam: false, params: {}}, {avoidReload: true, replace: true}).catch(() => {}); this.showNewSiteModal(state.params?.planType); } + + G.window.resetSeenPopups = (seen = false) => { + this.dismissedPopups.set(seen ? DismissedPopup.values : []); + }; } public get planName() { @@ -337,6 +343,18 @@ export class AppModelImpl extends Disposable implements AppModel { } } + public dismissedPopup(name: DismissedPopup): Computed { + const computed = Computed.create(null, use => use(this.dismissedPopups).includes(name)); + computed.onWrite(value => { + if (value) { + markAsSeen(this.dismissedPopups, name); + } else { + markAsUnSeen(this.dismissedPopups, name); + } + }); + return computed; + } + /** * If the current user is a new user, record a sign-up event via Google Tag Manager. */ diff --git a/app/client/models/UserPrefs.ts b/app/client/models/UserPrefs.ts index ad29cf76..94a0f3a3 100644 --- a/app/client/models/UserPrefs.ts +++ b/app/client/models/UserPrefs.ts @@ -98,3 +98,17 @@ export function markAsSeen(seenIdsObs: Observable, itemId: T console.warn("Failed to save preference in markAsSeen", e); } } + +export function markAsUnSeen(seenIdsObs: Observable, itemId: T) { + const seenIds = seenIdsObs.get() || []; + try { + if (seenIds.includes(itemId)) { + const seen = new Set(seenIds); + seen.delete(itemId); + seenIdsObs.set([...seen].sort()); + } + } catch (e) { + // tslint:disable-next-line:no-console + console.warn("Failed to save preference in markAsUnSeen", e); + } +} diff --git a/app/client/ui/DocMenu.ts b/app/client/ui/DocMenu.ts index 38323bbe..847ba9cc 100644 --- a/app/client/ui/DocMenu.ts +++ b/app/client/ui/DocMenu.ts @@ -12,6 +12,7 @@ import {attachAddNewTip} from 'app/client/ui/AddNewTip'; import * as css from 'app/client/ui/DocMenuCss'; import {buildHomeIntro, buildWorkspaceIntro} from 'app/client/ui/HomeIntro'; import {buildUpgradeButton} from 'app/client/ui/ProductUpgrades'; +import {buildTutorialCard} from 'app/client/ui/TutorialCard'; import {buildPinnedDoc, createPinnedDocs} from 'app/client/ui/PinnedDocs'; import {shadowScroll} from 'app/client/ui/shadowScroll'; import {transition} from 'app/client/ui/transitions'; @@ -71,7 +72,12 @@ function attachWelcomePopups(home: HomeModel): (el: Element) => void { function createLoadedDocMenu(owner: IDisposableOwner, home: HomeModel) { const flashDocId = observable(null); const upgradeButton = buildUpgradeButton(owner, home.app); - return css.docList( + return css.docList( /* vbox */ + /* first line */ + dom.create(buildTutorialCard, { app: home.app }), + /* hbox */ + css.docListContent( + /* left column - grow 1 */ css.docMenu( attachAddNewTip(home), @@ -169,7 +175,7 @@ function createLoadedDocMenu(owner: IDisposableOwner, home: HomeModel) { ), dom.maybe(use => !use(isNarrowScreenObs()) && ['all', 'workspace'].includes(use(home.currentPage)), () => upgradeButton.showUpgradeCard(css.upgradeCard.cls(''))), - ); + )); } function buildAllDocsBlock( diff --git a/app/client/ui/DocMenuCss.ts b/app/client/ui/DocMenuCss.ts index aa10acc2..c6034fae 100644 --- a/app/client/ui/DocMenuCss.ts +++ b/app/client/ui/DocMenuCss.ts @@ -8,11 +8,6 @@ import {bigBasicButton} from 'app/client/ui2018/buttons'; // styles, which gives it priority. import 'popweasel'; -export const docMenu = styled('div', ` - flex-grow: 1; - max-width: 100%; -`); - // The "&:after" clause forces some padding below all docs. export const docList = styled('div', ` height: 100%; @@ -20,6 +15,7 @@ export const docList = styled('div', ` overflow-y: auto; position: relative; display: flex; + flex-direction: column; &:after { content: ""; @@ -38,6 +34,15 @@ export const docList = styled('div', ` } `); +export const docListContent = styled('div', ` + display: flex; +`); + +export const docMenu = styled('div', ` + flex-grow: 1; + max-width: 100%; +`); + const listHeader = styled('div', ` min-height: 32px; line-height: 32px; diff --git a/app/client/ui/HomeLeftPane.ts b/app/client/ui/HomeLeftPane.ts index 7e378ed8..96104e63 100644 --- a/app/client/ui/HomeLeftPane.ts +++ b/app/client/ui/HomeLeftPane.ts @@ -16,7 +16,7 @@ import {testId, theme} from 'app/client/ui2018/cssVars'; import {icon} from 'app/client/ui2018/icons'; import {menu, menuIcon, menuItem, upgradableMenuItem, upgradeText} from 'app/client/ui2018/menus'; import {confirmModal} from 'app/client/ui2018/modals'; -import {shouldHideUiElement} from 'app/common/gristUrls'; +import {commonUrls, shouldHideUiElement} from 'app/common/gristUrls'; import * as roles from 'app/common/roles'; import {Workspace} from 'app/common/UserAPI'; import {computed, dom, domComputed, DomElementArg, observable, Observable, styled} from 'grainjs'; @@ -111,19 +111,26 @@ export function createHomeLeftPane(leftPanelOpen: Observable, home: Hom cssPageEntry( dom.hide(shouldHideUiElement("templates")), cssPageEntry.cls('-selected', (use) => use(home.currentPage) === "templates"), - cssPageLink(cssPageIcon('FieldTable'), cssLinkText(t("Examples & Templates")), + cssPageLink(cssPageIcon('Board'), cssLinkText(t("Examples & Templates")), urlState().setLinkUrl({homePage: "templates"}), testId('dm-templates-page'), ), ), cssPageEntry( cssPageEntry.cls('-selected', (use) => use(home.currentPage) === "trash"), - cssPageLink(cssPageIcon('Remove'), cssLinkText(t("Trash")), + cssPageLink(cssPageIcon('RemoveBig'), cssLinkText(t("Trash")), urlState().setLinkUrl({homePage: "trash"}), testId('dm-trash'), ), ), cssSpacer(), + cssPageEntry( + dom.hide(shouldHideUiElement("templates")), + cssPageLink(cssPageIcon('Bookmark'), cssLinkText(t("Tutorial")), + { href: commonUrls.basicTutorial, target: '_blank' }, + testId('dm-basic-tutorial'), + ), + ), createVideoTourToolsButton(), createHelpTools(home.app), ) diff --git a/app/client/ui/RightPanel.ts b/app/client/ui/RightPanel.ts index c3883f1a..676d27f1 100644 --- a/app/client/ui/RightPanel.ts +++ b/app/client/ui/RightPanel.ts @@ -758,7 +758,7 @@ const cssSeparator = styled('div', ` margin-top: 16px; `); -const cssConfigContainer = styled('div', ` +const cssConfigContainer = styled('div.test-config-container', ` overflow: auto; --color-list-item: none; --color-list-item-hover: none; diff --git a/app/client/ui/TutorialCard.ts b/app/client/ui/TutorialCard.ts new file mode 100644 index 00000000..81d72b5a --- /dev/null +++ b/app/client/ui/TutorialCard.ts @@ -0,0 +1,219 @@ +import {AppModel} from 'app/client/models/AppModel'; +import {bigPrimaryButton} from 'app/client/ui2018/buttons'; +import {isNarrowScreenObs, theme} from 'app/client/ui2018/cssVars'; +import {icon} from 'app/client/ui2018/icons'; +import {commonUrls, shouldHideUiElement} from 'app/common/gristUrls'; +import {Computed, dom, IDisposableOwner, makeTestId, styled} from 'grainjs'; + +const testId = makeTestId('test-tutorial-card-'); + +interface Options { + app: AppModel, + onStart?: () => void, +} + +export function buildTutorialCard(owner: IDisposableOwner, options: Options) { + const {app, onStart} = options; + const dismissed = app.dismissedPopup('tutorialFirstCard'); + owner.autoDispose(dismissed); + function onClose() { + dismissed.set(true); + } + const visible = Computed.create(owner, (use) => + !use(dismissed) + && !use(isNarrowScreenObs()) + && !shouldHideUiElement("templates") + ); + return dom.maybe(visible, () => { + return cssCard( + cssCaption( + dom('div', cssNewToGrist("New to Grist?")), + cssRelative( + cssStartHere("Start here."), + cssArrow() + ), + ), + cssContent( + testId('content'), + cssImage({src: commonUrls.basicTutorialImage}), + cssCardText( + cssLine(cssTitle("Grist Basics Tutorial")), + cssLine("Learn the basics of reference columns, linked widgets, column types, & cards."), + cssLine(cssSub('Beginner - 10 mins')), + cssButtonWrapper( + cssButtonWrapper.cls('-small'), + cssHeroButton("Start Tutorial"), + dom.on('click', () => onStart?.()) + ), + ), + ), + cssButtonWrapper( + cssButtonWrapper.cls('-big'), + cssHeroButton("Start Tutorial"), + {href: commonUrls.basicTutorial, target: '_blank'}, + ), + cssCloseButton(icon('CrossBig'), dom.on('click', () => onClose?.()), testId('close')), + ); + }); +} + +const cssContent = styled('div', ` + position: relative; + display: flex; + align-items: flex-start; + padding-top: 24px; + padding-bottom: 20px; + padding-right: 20px; + max-width: 460px; +`); + +const cssCardText = styled('div', ` + display: flex; + flex-direction: column; + justify-content: center; + align-self: stretch; + margin-left: 12px; +`); + +const cssRelative = styled('div', ` + position: relative; +`); + +const cssNewToGrist = styled('span', ` + font-style: normal; + font-weight: 400; + font-size: 24px; + line-height: 16px; + letter-spacing: 0.2px; + white-space: nowrap; +`); + +const cssStartHere = styled('span', ` + font-style: normal; + font-weight: 700; + font-size: 24px; + line-height: 16px; + letter-spacing: 0.2px; + white-space: nowrap; +`); + +const cssCaption = styled('div', ` + display: flex; + flex-direction: column; + gap: 12px; + margin-left: 32px; + margin-top: 42px; + margin-right: 64px; +`); + +const cssTitle = styled('span', ` + font-weight: 600; + font-size: 20px; +`); + +const cssSub = styled('span', ` + font-size: 12px; + color: ${theme.lightText}; +`); + +const cssLine = styled('div', ` + margin-bottom: 6px; +`); + +const cssHeroButton = styled(bigPrimaryButton, ` +`); + +const cssButtonWrapper = styled('a', ` + flex-grow: 1; + display: flex; + justify-content: flex-end; + margin-right: 60px; + align-items: center; + text-decoration: none; + &:hover { + text-decoration: none; + } + &-big .${cssHeroButton.className} { + padding: 16px 28px; + font-weight: 600; + font-size: 20px; + line-height: 1em; + } +`); + +const cssCloseButton = styled('div', ` + flex-shrink: 0; + align-self: flex-end; + cursor: pointer; + --icon-color: ${theme.controlSecondaryFg}; + margin: 8px 8px 4px 0px; + padding: 2px; + border-radius: 4px; + position: absolute; + top: 0; + right: 0; + &:hover { + background-color: ${theme.lightHover}; + } + &:active { + background-color: ${theme.hover}; + } +`); + +const cssImage = styled('img', ` + width: 187px; + height: 145px; + flex: none; +`); + +const cssArrow = styled('div', ` + position: absolute; + background-image: var(--icon-GreenArrow); + width: 94px; + height: 12px; + top: calc(50% - 6px); + left: calc(100% - 12px); + z-index: 1; +`); + + +const cssCard = styled('div', ` + display: flex; + position: relative; + color: ${theme.text}; + border-radius: 3px; + margin-bottom: 24px; + max-width: 1000px; + box-shadow: 0 2px 18px 0 ${theme.modalInnerShadow}, 0 0 1px 0 ${theme.modalOuterShadow}; + & .${cssButtonWrapper.className}-small { + display: none; + } + @media (max-width: 1320px) { + & .${cssButtonWrapper.className}-small { + flex-direction: column; + display: flex; + margin-top: 14px; + align-self: flex-start; + } + & .${cssButtonWrapper.className}-big { + display: none; + } + } + @media (max-width: 1000px) { + & .${cssArrow.className} { + display: none; + } + & .${cssCaption.className} { + flex-direction: row; + margin-bottom: 24px; + } + & { + flex-direction: column; + } + & .${cssContent.className} { + padding: 12px; + max-width: 100%; + margin-bottom: 28px; + } + } +`); diff --git a/app/client/ui2018/IconList.ts b/app/client/ui2018/IconList.ts index f51bfb71..b5be98da 100644 --- a/app/client/ui2018/IconList.ts +++ b/app/client/ui2018/IconList.ts @@ -39,6 +39,8 @@ export type IconName = "ChartArea" | "AddUser" | "BarcodeQR" | "BarcodeQR2" | + "Board" | + "Bookmark" | "CenterAlign" | "Chat" | "Code" | @@ -68,6 +70,7 @@ export type IconName = "ChartArea" | "FontStrikethrough" | "FontUnderline" | "FunctionResult" | + "GreenArrow" | "Grow" | "Help" | "Home" | @@ -105,6 +108,7 @@ export type IconName = "ChartArea" | "PublicFilled" | "Redo" | "Remove" | + "RemoveBig" | "Repl" | "ResizePanel" | "Revert" | @@ -176,6 +180,8 @@ export const IconList: IconName[] = ["ChartArea", "AddUser", "BarcodeQR", "BarcodeQR2", + "Board", + "Bookmark", "CenterAlign", "Chat", "Code", @@ -205,6 +211,7 @@ export const IconList: IconName[] = ["ChartArea", "FontStrikethrough", "FontUnderline", "FunctionResult", + "GreenArrow", "Grow", "Help", "Home", @@ -242,6 +249,7 @@ export const IconList: IconName[] = ["ChartArea", "PublicFilled", "Redo", "Remove", + "RemoveBig", "Repl", "ResizePanel", "Revert", diff --git a/app/common/Prefs.ts b/app/common/Prefs.ts index 1a676782..41abddff 100644 --- a/app/common/Prefs.ts +++ b/app/common/Prefs.ts @@ -100,8 +100,9 @@ export interface BehavioralPromptPrefs { * List of all popups that user can see and dismiss */ export const DismissedPopup = StringUnion( - 'deleteRecords', // confirmation for deleting records keyboard shortcut - 'deleteFields', // confirmation for deleting columns keyboard shortcut + 'deleteRecords', // confirmation for deleting records keyboard shortcut, + 'deleteFields', // confirmation for deleting columns keyboard shortcut, + 'tutorialFirstCard', // first card of the tutorial, ); export type DismissedPopup = typeof DismissedPopup.type; diff --git a/app/common/gristUrls.ts b/app/common/gristUrls.ts index 8aae2399..15dcd9db 100644 --- a/app/common/gristUrls.ts +++ b/app/common/gristUrls.ts @@ -77,6 +77,8 @@ export const commonUrls = { efcrConnect: 'https://efc-r.com/connect', efcrHelp: 'https://www.nioxus.info/eFCR-Help', + basicTutorial: 'https://templates.getgrist.com/woXtXUBmiN5T/Grist-Basics', + basicTutorialImage: 'https://www.getgrist.com/wp-content/uploads/2021/08/lightweight-crm.png', }; /** diff --git a/static/icons/icons.css b/static/icons/icons.css index bfc303bf..8f98ccf0 100644 --- a/static/icons/icons.css +++ b/static/icons/icons.css @@ -40,6 +40,8 @@ --icon-AddUser: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTYiIHdpZHRoPSIxNiI+PGcgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgY2xhc3M9Im5jLWljb24td3JhcHBlciIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMjEyMTIxIiBzdHJva2UtbGluZWNhcD0icm91bmQiPjxjaXJjbGUgY3g9IjciIGN5PSI0IiByPSIzLjUiIGRhdGEtY2FwPSJidXR0Ii8+PHBhdGggZD0iTTkuODk0IDEwQTUuOTggNS45OCAwIDAgMCA3LjUgOS41aC0xYTYgNiAwIDAgMC02IDZoOU0xMi41IDkuNXY2TTkuNSAxMi41aDYiIGRhdGEtY2FwPSJidXR0Ii8+PC9nPjwvc3ZnPg=='); --icon-BarcodeQR: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTYuNS41SC41VjYuNUg2LjVWLjV6TTYuNSA5LjVILjVWMTUuNUg2LjVWOS41ek0xNS41LjVIOS41VjYuNUgxNS41Vi41ek0xNS41IDE1LjVIOS41TTExLjUgOS41SDE1LjVWMTIuNSIgc3Ryb2tlPSIjMjEyMTIxIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjwvc3ZnPg=='); --icon-BarcodeQR2: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTYgN0gxQy40IDcgMCA2LjYgMCA2VjFDMCAuNC40IDAgMSAwSDZDNi42IDAgNyAuNCA3IDFWNkM3IDYuNiA2LjYgNyA2IDd6TTIgNUg1VjJIMlY1ek02IDE2SDFDLjQgMTYgMCAxNS42IDAgMTVWMTBDMCA5LjQuNCA5IDEgOUg2QzYuNiA5IDcgOS40IDcgMTBWMTVDNyAxNS42IDYuNiAxNiA2IDE2ek0yIDE0SDVWMTFIMlYxNHpNMTUgN0gxMEM5LjQgNyA5IDYuNiA5IDZWMUM5IC40IDkuNCAwIDEwIDBIMTVDMTUuNiAwIDE2IC40IDE2IDFWNkMxNiA2LjYgMTUuNiA3IDE1IDd6TTExIDVIMTRWMkgxMVY1eiIgZmlsbD0iIzE2QjM3OCIvPjxwYXRoIGQ9Ik00IDNIM1Y0SDRWM3pNNCAxMkgzVjEzSDRWMTJ6TTE2IDE0SDlWMTZIMTZWMTR6TTE2IDEzSDE0VjExSDExVjlIMTVDMTUuNiA5IDE2IDkuNCAxNiAxMFYxM3oiIGZpbGw9IiMxNkIzNzgiLz48cGF0aCBkPSJNMTMgMTJIMTJWMTNIMTNWMTJ6TTEzIDNIMTJWNEgxM1YzeiIgZmlsbD0iIzE2QjM3OCIvPjwvc3ZnPg=='); + --icon-Board: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PGcgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMjYyNjMzIiBzdHJva2UtbGluZWNhcD0icm91bmQiIGNsYXNzPSJuYy1pY29uLXdyYXBwZXIiPjxyZWN0IHg9Ii41IiB5PSIuNSIgd2lkdGg9IjE1IiBoZWlnaHQ9IjE1IiByeD0iMSIvPjxwYXRoIGQ9Ik0zLjUgMy41aDN2M2gtM3pNMy41IDkuNWgzdjNoLTN6TTkuNSAzLjVoM3Y5aC0zeiIvPjwvZz48L3N2Zz4='); + --icon-Bookmark: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PGcgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMjYyNjMzIiBzdHJva2UtbGluZWNhcD0icm91bmQiIGNsYXNzPSJuYy1pY29uLXdyYXBwZXIiPjxwYXRoIGRhdGEtY2FwPSJidXR0IiBkPSJNOC41LjV2N0w2IDUuNWwtMi41IDJ2LTciLz48cGF0aCBkPSJNLjUgMTRWMi41YTIgMiAwIDAgMSAyLTJoMTFhMiAyIDAgMCAxIDIgMlYxNCIgZGF0YS1jYXA9ImJ1dHQiLz48cmVjdCB4PSIuNSIgeT0iMTIuNSIgd2lkdGg9IjE1IiBoZWlnaHQ9IjMiIHJ4PSIxLjUiIHJ5PSIxLjUiIGRhdGEtY2FwPSJidXR0Ii8+PC9nPjwvc3ZnPg=='); --icon-CenterAlign: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTUuNSw4LjUgQzUuMjIzODU3NjMsOC41IDUsOC4yNzYxNDIzNyA1LDggQzUsNy43MjM4NTc2MyA1LjIyMzg1NzYzLDcuNSA1LjUsNy41IEwxMC41LDcuNSBDMTAuNzc2MTQyNCw3LjUgMTEsNy43MjM4NTc2MyAxMSw4IEMxMSw4LjI3NjE0MjM3IDEwLjc3NjE0MjQsOC41IDEwLjUsOC41IEw1LjUsOC41IFogTTIuNSw0IEMyLjIyMzg1NzYzLDQgMiwzLjc3NjE0MjM3IDIsMy41IEMyLDMuMjIzODU3NjMgMi4yMjM4NTc2MywzIDIuNSwzIEwxMy41LDMgQzEzLjc3NjE0MjQsMyAxNCwzLjIyMzg1NzYzIDE0LDMuNSBDMTQsMy43NzYxNDIzNyAxMy43NzYxNDI0LDQgMTMuNSw0IEwyLjUsNCBaIE00LDEzIEMzLjcyMzg1NzYzLDEzIDMuNSwxMi43NzYxNDI0IDMuNSwxMi41IEMzLjUsMTIuMjIzODU3NiAzLjcyMzg1NzYzLDEyIDQsMTIgTDEyLDEyIEMxMi4yNzYxNDI0LDEyIDEyLjUsMTIuMjIzODU3NiAxMi41LDEyLjUgQzEyLjUsMTIuNzc2MTQyNCAxMi4yNzYxNDI0LDEzIDEyLDEzIEw0LDEzIFoiIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ZnPg=='); --icon-Chat: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTkuNTYyIDNhNy41IDcuNSAwIDAgMC02Ljc5OCAxMC42NzNsLS43MjQgMi44NDJhMS4yNSAxLjI1IDAgMCAwIDEuNTA0IDEuNTI0Yy43NS0uMTggMS45MDMtLjQ1NyAyLjkzLS43MDJBNy41IDcuNSAwIDEgMCA5LjU2MSAzWm0tNiA3LjVhNiA2IDAgMSAxIDMuMzMgNS4zNzVsLS4yNDQtLjEyMS0uMjY0LjA2M2MtLjkyMy4yMi0xLjk5LjQ3NS0yLjc4OC42NjdsLjY5LTIuNzA4LjA3LS4yNzYtLjEzLS4yNTNhNS45NzEgNS45NzEgMCAwIDEtLjY2NC0yLjc0N1ptMTEgMTAuNWMtMS45NyAwLTMuNzYyLS43NTktNS4xLTJoLjFjLjcxOCAwIDEuNDE1LS4wODkgMi4wOC0uMjU3Ljg2NS40ODIgMS44Ni43NTcgMi45Mi43NTcuOTYgMCAxLjg2Ni0uMjI1IDIuNjctLjYyNWwuMjQzLS4xMjEuMjY0LjA2M2MuOTIyLjIyIDEuOTY2LjQ0NSAyLjc0LjYxLS4xNzUtLjc1MS0uNDE0LTEuNzU2LS42NDItMi42NTFsLS4wNy0uMjc2LjEzLS4yNTNhNS45NzEgNS45NzEgMCAwIDAgLjY2NS0yLjc0NyA1Ljk5NSA1Ljk5NSAwIDAgMC0yLjc0Ny01LjA0MiA4LjQ0IDguNDQgMCAwIDAtLjgtMi4wNDcgNy41MDMgNy41MDMgMCAwIDEgNC4zNDQgMTAuMjYzYy4yNTMgMS4wMDguNTA5IDIuMS42NzEgMi44MDNhMS4yNDQgMS4yNDQgMCAwIDEtMS40NjcgMS41IDEzMi42MiAxMzIuNjIgMCAwIDEtMi45MTMtLjY0IDcuNDc2IDcuNDc2IDAgMCAxLTMuMDg4LjY2M1oiIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ZnPg=='); --icon-Code: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTEwLjAzMzMyNzIsMC4zMjA1MTA0NiBDMTAuMTMyNDU2NSwwLjA2Mjc3NDE4OCAxMC40MjE3NTMzLC0wLjA2NTgwMjEzODYgMTAuNjc5NDg5NSwwLjAzMzMyNzE5NjkgQzEwLjkzNzIyNTgsMC4xMzI0NTY1MzIgMTEuMDY1ODAyMSwwLjQyMTc1MzI2NyAxMC45NjY2NzI4LDAuNjc5NDg5NTQgTDUuOTY2NjcyOCwxMy42Nzk0ODk1IEM1Ljg2NzU0MzQ3LDEzLjkzNzIyNTggNS41NzgyNDY3MywxNC4wNjU4MDIxIDUuMzIwNTEwNDYsMTMuOTY2NjcyOCBDNS4wNjI3NzQxOSwxMy44Njc1NDM1IDQuOTM0MTk3ODYsMTMuNTc4MjQ2NyA1LjAzMzMyNzIsMTMuMzIwNTEwNSBMMTAuMDMzMzI3MiwwLjMyMDUxMDQ2IFogTTMuODUzNTUzMzksOS4xNDY0NDY2MSBDNC4wNDg4MTU1NCw5LjM0MTcwODc2IDQuMDQ4ODE1NTQsOS42NTgyOTEyNCAzLjg1MzU1MzM5LDkuODUzNTUzMzkgQzMuNjU4MjkxMjQsMTAuMDQ4ODE1NSAzLjM0MTcwODc2LDEwLjA0ODgxNTUgMy4xNDY0NDY2MSw5Ljg1MzU1MzM5IEwwLjE0NjQ0NjYwOSw2Ljg1MzU1MzM5IEMtMC4wNDg4MTU1MzY1LDYuNjU4MjkxMjQgLTAuMDQ4ODE1NTM2NSw2LjM0MTcwODc2IDAuMTQ2NDQ2NjA5LDYuMTQ2NDQ2NjEgTDMuMTQ2NDQ2NjEsMy4xNDY0NDY2MSBDMy4zNDE3MDg3NiwyLjk1MTE4NDQ2IDMuNjU4MjkxMjQsMi45NTExODQ0NiAzLjg1MzU1MzM5LDMuMTQ2NDQ2NjEgQzQuMDQ4ODE1NTQsMy4zNDE3MDg3NiA0LjA0ODgxNTU0LDMuNjU4MjkxMjQgMy44NTM1NTMzOSwzLjg1MzU1MzM5IEwxLjIwNzEwNjc4LDYuNSBMMy44NTM1NTMzOSw5LjE0NjQ0NjYxIFogTTEyLjE0NjQ0NjYsMy44NTM1NTMzOSBDMTEuOTUxMTg0NSwzLjY1ODI5MTI0IDExLjk1MTE4NDUsMy4zNDE3MDg3NiAxMi4xNDY0NDY2LDMuMTQ2NDQ2NjEgQzEyLjM0MTcwODgsMi45NTExODQ0NiAxMi42NTgyOTEyLDIuOTUxMTg0NDYgMTIuODUzNTUzNCwzLjE0NjQ0NjYxIEwxNS44NTM1NTM0LDYuMTQ2NDQ2NjEgQzE2LjA0ODgxNTUsNi4zNDE3MDg3NiAxNi4wNDg4MTU1LDYuNjU4MjkxMjQgMTUuODUzNTUzNCw2Ljg1MzU1MzM5IEwxMi44NTM1NTM0LDkuODUzNTUzMzkgQzEyLjY1ODI5MTIsMTAuMDQ4ODE1NSAxMi4zNDE3MDg4LDEwLjA0ODgxNTUgMTIuMTQ2NDQ2Niw5Ljg1MzU1MzM5IEMxMS45NTExODQ1LDkuNjU4MjkxMjQgMTEuOTUxMTg0NSw5LjM0MTcwODc2IDEyLjE0NjQ0NjYsOS4xNDY0NDY2MSBMMTQuNzkyODkzMiw2LjUgTDEyLjE0NjQ0NjYsMy44NTM1NTMzOSBaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIDEpIiBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz48L3N2Zz4='); @@ -69,6 +71,7 @@ --icon-FontStrikethrough: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PHBhdGggZD0iTSAxLjgwNTY1ODUsMy4zOTcxNzIgViAxLjczNTc0MzEgSCAxNC4xOTQzNDIgViAzLjM5NzE3MiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjOTI5Mjk5IiBzdHJva2Utd2lkdGg9IjEuMTQzIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNIDcuOTk5OTk5OSwxMC4wNDI4ODcgViAxNC4xOTY0NiIgc3Ryb2tlPSIjOTI5Mjk5IiBzdHJva2Utd2lkdGg9IjEuMTQzIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNIDcuOTk5OTk5OSwxLjczNTc0MzEgViA3LjU1MDc0NCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjOTI5Mjk5IiBzdHJva2Utd2lkdGg9IjEuMTQzIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNNS4zNDUyODIyIDE0LjE5NjQ2SDEwLjY1NDcxOE0xLjgwNTY1ODUgNy41NTA3NDRIMTQuMTk0MzQyIiBzdHJva2U9IiM5MjkyOTkiIHN0cm9rZS13aWR0aD0iMS4xNDMiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjwvc3ZnPg=='); --icon-FontUnderline: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PGRlZnM+PGNsaXBQYXRoIGlkPSJhIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMCAwSDEyVjEySDB6Ii8+PC9jbGlwUGF0aD48L2RlZnM+PGcgdHJhbnNmb3JtPSJtYXRyaXgoMS4xNDI5NSAwIDAgMS4xNDI5NSAxLjE0MiAuNzIxKSIgY2xpcC1wYXRoPSJ1cmwoI2EpIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cGF0aCBkPSJNLjM3NSAxMS42MjVoMTEuMjVNOS4zNzUgMS4xMjVWNkM5LjM3NSA2Ljg5NTExIDkuMDE5NDIgNy43NTM1NSA4LjM4NjQ5IDguMzg2NDkgNy43NTM1NSA5LjAxOTQyIDYuODk1MTEgOS4zNzUgNiA5LjM3NXYwQzUuMTA0ODkgOS4zNzUgNC4yNDY0NSA5LjAxOTQyIDMuNjEzNTEgOC4zODY0OSAyLjk4MDU4IDcuNzUzNTUgMi42MjUgNi44OTUxMSAyLjYyNSA2VjEuMTI1TTEuMTI1IDEuMTI1aDNNNy44NzUgMS4xMjVoMyIvPjwvZz48L3N2Zz4='); --icon-FunctionResult: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTQsMiBMMTIsMiBDMTMuMTA0NTY5NSwyIDE0LDIuODk1NDMwNSAxNCw0IEwxNCwxMiBDMTQsMTMuMTA0NTY5NSAxMy4xMDQ1Njk1LDE0IDEyLDE0IEw0LDE0IEMyLjg5NTQzMDUsMTQgMiwxMy4xMDQ1Njk1IDIsMTIgTDIsNCBDMiwyLjg5NTQzMDUgMi44OTU0MzA1LDIgNCwyIFogTTQuNSw2IEM0LjIyMzg1NzYzLDYgNCw2LjIyMzg1NzYzIDQsNi41IEM0LDYuNzc2MTQyMzcgNC4yMjM4NTc2Myw3IDQuNSw3IEwxMS41LDcgQzExLjc3NjE0MjQsNyAxMiw2Ljc3NjE0MjM3IDEyLDYuNSBDMTIsNi4yMjM4NTc2MyAxMS43NzYxNDI0LDYgMTEuNSw2IEw0LjUsNiBaIE00LjUsOSBDNC4yMjM4NTc2Myw5IDQsOS4yMjM4NTc2MyA0LDkuNSBDNCw5Ljc3NjE0MjM3IDQuMjIzODU3NjMsMTAgNC41LDEwIEwxMS41LDEwIEMxMS43NzYxNDI0LDEwIDEyLDkuNzc2MTQyMzcgMTIsOS41IEMxMiw5LjIyMzg1NzYzIDExLjc3NjE0MjQsOSAxMS41LDkgTDQuNSw5IFoiIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ZnPg=='); + --icon-GreenArrow: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOTQiIGhlaWdodD0iMTIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTk0IDZMODQgMC4yMjY0OTdWMTEuNzczNUw5NCA2Wk0wIDdINC43VjVIMFY3Wk0xNC4xIDdIMjMuNVY1SDE0LjFWN1pNMzIuOSA3SDQyLjNWNUgzMi45VjdaTTUxLjcgN0g2MS4xVjVINTEuN1Y3Wk03MC41IDdINzkuOVY1SDcwLjVWN1oiIGZpbGw9IiMxNkIzNzgiLz48L3N2Zz4='); --icon-Grow: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTEuNSAxLjVMNi41IDYuNU05LjUgOS41TDE0LjUgMTQuNU03LjUgMS41SDEuNVY3LjVNMTQuNSA4LjVWMTQuNUg4LjUiIHN0cm9rZT0iIzkyOTI5OSIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48L3N2Zz4='); --icon-Help: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTIuNzA5MjY0NDMsMy40MTYzNzEyMSBDMS42NDQ0MDc2OSw0LjY0NDQzMjIyIDEsNi4yNDY5NjEzNyAxLDggQzEsOS43NTMwMzg2MyAxLjY0NDQwNzY5LDExLjM1NTU2NzggMi43MDkyNjQ0MywxMi41ODM2Mjg4IEw0Ljg0MDA3MTI5LDEwLjQ1MjgyMTkgQzQuMzEzNTQwOCw5Ljc3NTQ4MDA3IDQsOC45MjQzNTU3MyA0LDggQzQsNy4wNzU2NDQyNyA0LjMxMzU0MDgsNi4yMjQ1MTk5MyA0Ljg0MDA3MTI5LDUuNTQ3MTc4MDcgTDIuNzA5MjY0NDMsMy40MTYzNzEyMSBaIE0zLjQxNjM3MTIxLDIuNzA5MjY0NDMgTDUuNTQ3MTc4MDcsNC44NDAwNzEyOSBDNi4yMjQ1MTk5Myw0LjMxMzU0MDggNy4wNzU2NDQyNyw0IDgsNCBDOC45MjQzNTU3Myw0IDkuNzc1NDgwMDcsNC4zMTM1NDA4IDEwLjQ1MjgyMTksNC44NDAwNzEyOSBMMTIuNTgzNjI4OCwyLjcwOTI2NDQzIEMxMS4zNTU1Njc4LDEuNjQ0NDA3NjkgOS43NTMwMzg2MywxIDgsMSBDNi4yNDY5NjEzNywxIDQuNjQ0NDMyMjIsMS42NDQ0MDc2OSAzLjQxNjM3MTIxLDIuNzA5MjY0NDMgWiBNNS45MDE5ODczMSw1Ljg1NTYyMzc2IEM1Ljg5NDgyMDAxLDUuODYzNzgzMDcgNS44ODczNDIwNCw1Ljg3MTc2NDc0IDUuODc5NTUzMzksNS44Nzk1NTMzOSBDNS44NzE3NjQ3NCw1Ljg4NzM0MjA0IDUuODYzNzgzMDcsNS44OTQ4MjAwMSA1Ljg1NTYyMzc2LDUuOTAxOTg3MzEgQzUuMzI2Mjk1NSw2LjQ0MjkzOTMyIDUsNy4xODMzNjQ0NiA1LDggQzUsOC44MTY2MzU1NCA1LjMyNjI5NTUsOS41NTcwNjA2OCA1Ljg1NTYyMzc2LDEwLjA5ODAxMjcgQzUuODYzNzgzMDcsMTAuMTA1MTggNS44NzE3NjQ3NCwxMC4xMTI2NTggNS44Nzk1NTMzOSwxMC4xMjA0NDY2IEM1Ljg4NzM0MjA0LDEwLjEyODIzNTMgNS44OTQ4MjAwMSwxMC4xMzYyMTY5IDUuOTAxOTg3MzEsMTAuMTQ0Mzc2MiBDNi40NDI5MzkzMiwxMC42NzM3MDQ1IDcuMTgzMzY0NDYsMTEgOCwxMSBDOC44MTY2MzU1NCwxMSA5LjU1NzA2MDY4LDEwLjY3MzcwNDUgMTAuMDk4MDEyNywxMC4xNDQzNzYyIEMxMC4xMDUxOCwxMC4xMzYyMTY5IDEwLjExMjY1OCwxMC4xMjgyMzUzIDEwLjEyMDQ0NjYsMTAuMTIwNDQ2NiBDMTAuMTI4MjM1MywxMC4xMTI2NTggMTAuMTM2MjE2OSwxMC4xMDUxOCAxMC4xNDQzNzYyLDEwLjA5ODAxMjcgQzEwLjY3MzcwNDUsOS41NTcwNjA2OCAxMSw4LjgxNjYzNTU0IDExLDggQzExLDcuMTgzMzY0NDYgMTAuNjczNzA0NSw2LjQ0MjkzOTMyIDEwLjE0NDM3NjIsNS45MDE5ODczMSBDMTAuMTM2MjE2OSw1Ljg5NDgyMDAxIDEwLjEyODIzNTMsNS44ODczNDIwNCAxMC4xMjA0NDY2LDUuODc5NTUzMzkgQzEwLjExMjY1OCw1Ljg3MTc2NDc0IDEwLjEwNTE4LDUuODYzNzgzMDcgMTAuMDk4MDEyNyw1Ljg1NTYyMzc2IEM5LjU1NzA2MDY4LDUuMzI2Mjk1NSA4LjgxNjYzNTU0LDUgOCw1IEM3LjE4MzM2NDQ2LDUgNi40NDI5MzkzMiw1LjMyNjI5NTUgNS45MDE5ODczMSw1Ljg1NTYyMzc2IFogTTMuNDE2MzcxMjEsMTMuMjkwNzM1NiBDNC42NDQ0MzIyMiwxNC4zNTU1OTIzIDYuMjQ2OTYxMzcsMTUgOCwxNSBDOS43NTMwMzg2MywxNSAxMS4zNTU1Njc4LDE0LjM1NTU5MjMgMTIuNTgzNjI4OCwxMy4yOTA3MzU2IEwxMC40NTI4MjE5LDExLjE1OTkyODcgQzkuNzc1NDgwMDcsMTEuNjg2NDU5MiA4LjkyNDM1NTczLDEyIDgsMTIgQzcuMDc1NjQ0MjcsMTIgNi4yMjQ1MTk5MywxMS42ODY0NTkyIDUuNTQ3MTc4MDcsMTEuMTU5OTI4NyBMMy40MTYzNzEyMSwxMy4yOTA3MzU2IFogTTEzLjI5MDczNTYsMTIuNTgzNjI4OCBDMTQuMzU1NTkyMywxMS4zNTU1Njc4IDE1LDkuNzUzMDM4NjMgMTUsOCBDMTUsNi4yNDY5NjEzNyAxNC4zNTU1OTIzLDQuNjQ0NDMyMjIgMTMuMjkwNzM1NiwzLjQxNjM3MTIxIEwxMS4xNTk5Mjg3LDUuNTQ3MTc4MDcgQzExLjY4NjQ1OTIsNi4yMjQ1MTk5MyAxMiw3LjA3NTY0NDI3IDEyLDggQzEyLDguOTI0MzU1NzMgMTEuNjg2NDU5Miw5Ljc3NTQ4MDA3IDExLjE1OTkyODcsMTAuNDUyODIxOSBMMTMuMjkwNzM1NiwxMi41ODM2Mjg4IFogTTgsMTYgQzMuNTgxNzIyLDE2IDAsMTIuNDE4Mjc4IDAsOCBDMCwzLjU4MTcyMiAzLjU4MTcyMiwwIDgsMCBDMTIuNDE4Mjc4LDAgMTYsMy41ODE3MjIgMTYsOCBDMTYsMTIuNDE4Mjc4IDEyLjQxODI3OCwxNiA4LDE2IFoiIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ZnPg=='); --icon-Home: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTgsMi4wODMwOTUxOSBMMS40NzE4MjUzMiw2IEw4LDkuOTE2OTA0ODEgTDE0LjUyODE3NDcsNiBMOCwyLjA4MzA5NTE5IFogTTguMjU3MjQ3ODgsMS4wNzEyNTM1NCBMMTUuNzU3MjQ3OSw1LjU3MTI1MzU0IEMxNi4wODA5MTc0LDUuNzY1NDU1MjMgMTYuMDgwOTE3NCw2LjIzNDU0NDc3IDE1Ljc1NzI0NzksNi40Mjg3NDY0NiBMOC4yNTcyNDc4OCwxMC45Mjg3NDY1IEM4LjA5ODkwNjY4LDExLjAyMzc1MTIgNy45MDEwOTMzMiwxMS4wMjM3NTEyIDcuNzQyNzUyMTIsMTAuOTI4NzQ2NSBMMC4yNDI3NTIxMjIsNi40Mjg3NDY0NiBDLTAuMDgwOTE3Mzc0MSw2LjIzNDU0NDc3IC0wLjA4MDkxNzM3NDEsNS43NjU0NTUyMyAwLjI0Mjc1MjEyMiw1LjU3MTI1MzU0IEw3Ljc0Mjc1MjEyLDEuMDcxMjUzNTQgQzcuOTAxMDkzMzIsMC45NzYyNDg4MjEgOC4wOTg5MDY2OCwwLjk3NjI0ODgyMSA4LjI1NzI0Nzg4LDEuMDcxMjUzNTQgWiBNMTQuNTI4MTc0NywxMCBMMTMuNzQyNzUyMSw5LjUyODc0NjQ2IEMxMy41MDU5NjIsOS4zODY2NzIzOCAxMy40MjkxNzk1LDkuMDc5NTQyMjYgMTMuNTcxMjUzNSw4Ljg0Mjc1MjEyIEMxMy43MTMzMjc2LDguNjA1OTYxOTkgMTQuMDIwNDU3Nyw4LjUyOTE3OTQ2IDE0LjI1NzI0NzksOC42NzEyNTM1NCBMMTUuNzU3MjQ3OSw5LjU3MTI1MzU0IEMxNi4wODA5MTc0LDkuNzY1NDU1MjMgMTYuMDgwOTE3NCwxMC4yMzQ1NDQ4IDE1Ljc1NzI0NzksMTAuNDI4NzQ2NSBMOC4yNTcyNDc4OCwxNC45Mjg3NDY1IEM4LjA5ODkwNjY4LDE1LjAyMzc1MTIgNy45MDEwOTMzMiwxNS4wMjM3NTEyIDcuNzQyNzUyMTIsMTQuOTI4NzQ2NSBMMC4yNDI3NTIxMjIsMTAuNDI4NzQ2NSBDLTAuMDgwOTE3Mzc0MSwxMC4yMzQ1NDQ4IC0wLjA4MDkxNzM3NDEsOS43NjU0NTUyMyAwLjI0Mjc1MjEyMiw5LjU3MTI1MzU0IEwxLjc0Mjc1MjEyLDguNjcxMjUzNTQgQzEuOTc5NTQyMjYsOC41MjkxNzk0NiAyLjI4NjY3MjM4LDguNjA1OTYxOTkgMi40Mjg3NDY0Niw4Ljg0Mjc1MjEyIEMyLjU3MDgyMDU0LDkuMDc5NTQyMjYgMi40OTQwMzgwMSw5LjM4NjY3MjM4IDIuMjU3MjQ3ODgsOS41Mjg3NDY0NiBMMS40NzE4MjUzMiwxMCBMOCwxMy45MTY5MDQ4IEwxNC41MjgxNzQ3LDEwIFoiIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ZnPg=='); @@ -106,6 +109,7 @@ --icon-PublicFilled: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48cGF0aCBkPSJNMCAwSDE2VjE2SDB6IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIC0xKSIvPjxnIGZpbGw9IiMxNkIzNzgiIGZpbGwtcnVsZT0ibm9uemVybyI+PHBhdGggZD0iTTE1LjI3NSwxMC4yOTMgTDEzLjA4Nyw5LjY2OCBDMTIuNzI2NDc4MSw5LjU2NDkxNzggMTIuNDUzOTIwNCw5LjI2ODgxMDUgMTIuMzgxLDguOTAxIEwxMi4yNDcsOC4yMjYgQzEzLjMxNDEyMDMsNy43MzgzMDYxNSAxMy45OTkwMDIxLDYuNjczMjgxNyAxNCw1LjUgTDE0LDQuMTI2IEMxNC4wMjUxOTc2LDIuNDczMDAzMjUgMTIuNzQyNTA1NSwxLjA5NDE1MzM3IDExLjA5MiwxIEM5Ljg3OTM3MDksMC45NjM0MTA4MjIgOC43NjQwNjU1LDEuNjYwNzcyNjkgOC4yNjYsMi43NjcgQzguNzQzMDQ3OCwzLjQ2MTI3ODUgOC45OTg5MTk4NCw0LjI4MzYyNDc0IDksNS4xMjYgTDksNi41IEM4Ljk5ODIwNzc0LDYuODYzNzcwNTcgOC45NDYwNDU1OCw3LjIyNTU0MDM4IDguODQ1LDcuNTc1IEM5LjEwNDIzMjUzLDcuODQ2OTQyMjYgOS40MTIyMzk1Myw4LjA2Nzc3MTA2IDkuNzUzLDguMjI2IEw5LjYxOSw4LjkgQzkuNTQ2MDc5NTcsOS4yNjc4MTA1IDkuMjczNTIxODcsOS41NjM5MTc4IDguOTEzLDkuNjY3IEw4LjA3LDkuOTA4IEw5LjU1LDEwLjMzMSBDMTAuNDA2OTY0MywxMC41Nzg1NTM2IDEwLjk5NzcwNTksMTEuMzYxOTk5MyAxMSwxMi4yNTQgTDExLDE0LjUgQzEwLjk5ODM0MjgsMTQuNjcwNzE5OCAxMC45NjcyMTg5LDE0LjgzOTg3MTUgMTAuOTA4LDE1IEwxNS41LDE1IEMxNS43NzYxNDI0LDE1IDE2LDE0Ljc3NjE0MjQgMTYsMTQuNSBMMTYsMTEuMjU0IEMxNS45OTk4MDE4LDEwLjgwNzc5MTggMTUuNzA0MDA0MywxMC40MTU3MDcyIDE1LjI3NSwxMC4yOTMgWiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtMSkiLz48cGF0aCBkPSJNOS4yNzUsMTEuMjkzIEw3LjA4NywxMC42NjggQzYuNzI2MjAyMzIsMTAuNTY0NzUzOSA2LjQ1MzU4MywxMC4yNjgxOTM1IDYuMzgxLDkuOSBMNi4yNDcsOS4yMjUgQzcuMzEzNzkxNDEsOC43Mzc0NTY5MyA3Ljk5ODYxMTI1LDcuNjcyOTE5NTQgOCw2LjUgTDgsNS4xMjYgQzguMDI1MTk3NTYsMy40NzMwMDMyNSA2Ljc0MjUwNTQ3LDIuMDk0MTUzMzcgNS4wOTIsMiBDNC4yODA1MzI3LDEuOTc1MTAzMiAzLjQ5MzYyMDIsMi4yODAxNDgyNyAyLjkxMDk1MzIzLDIuODQ1NDc4NDYgQzIuMzI4Mjg2MjUsMy40MTA4MDg2NCAxLjk5OTYxODE2LDQuMTg4MTUwOTUgMiw1IEwyLDYuNSBDMi4wMDA5OTc5MSw3LjY3MzI4MTcgMi42ODU4Nzk3NCw4LjczODMwNjE1IDMuNzUzLDkuMjI2IEwzLjYxOSw5LjkgQzMuNTQ2MDc5NTcsMTAuMjY3ODEwNSAzLjI3MzUyMTg3LDEwLjU2MzkxNzggMi45MTMsMTAuNjY3IEwwLjcyNSwxMS4yOTIgQzAuMjk1NjM5MjYyLDExLjQxNDgwOTEgLTAuMDAwMjQ4MTc3MTU3LDExLjgwNzQyMTIgMCwxMi4yNTQgTDAsMTQuNSBDMy4zODE3NjU4MWUtMTcsMTQuNzc2MTQyNCAwLjIyMzg1NzYyNSwxNSAwLjUsMTUgTDkuNSwxNSBDOS43NzYxNDIzNywxNSAxMCwxNC43NzYxNDI0IDEwLDE0LjUgTDEwLDEyLjI1NCBDOS45OTk4MDE3NywxMS44MDc3OTE4IDkuNzA0MDA0MzEsMTEuNDE1NzA3MiA5LjI3NSwxMS4yOTMgWiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtMSkiLz48L2c+PC9nPjwvc3ZnPg=='); --icon-Redo: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTIuMzU1MzI4LDUgTDYuNTAwMTAyMzgsNSBDNi43NzYyNDQ3Niw1IDcuMDAwMTAyMzgsNS4yMjM4NTc2MyA3LjAwMDEwMjM4LDUuNSBDNy4wMDAxMDIzOCw1Ljc3NjE0MjM3IDYuNzc2MjQ0NzYsNiA2LjUwMDEwMjM4LDYgTDEuNTEwMjEyMzgsNiBDMS40ODY4NTQ0NCw2LjAwMDQ5MTk4IDEuNDYzMzU4OTcsNS45OTkzNDQxNCAxLjQzOTg5NjI3LDUuOTk2NDk5MjUgQzEuMzYwMzg0NTQsNS45ODY4NzA1OCAxLjI4NjYwNzQzLDUuOTU4NjY1NjMgMS4yMjMwNzYwNCw1LjkxNjMwNDg0IEMxLjA5OTMxODAxLDUuODM0MTc1NDcgMS4wMjE5NjcwOSw1LjcwMzQ3ODggMS4wMDQwMTY1Niw1LjU2Mjg2NjI5IEMxLjAwMTkxMjc3LDUuNTQ2MDk2MDIgMS4wMDA2Mzk1Niw1LjUyOTA2NzQgMS4wMDAyMzk0Miw1LjUxMTgyMjkyIEMwLjk5OTk1NjM3MSw1LjUwNDI3MzIxIDAuOTk5OTQyNjg0LDUuNDk2NzA0NiAxLjAwMDEwMjM4LDUuNDg5MTI2OTMgTDEuMDAwMTAyMzgsMC41IEMxLjAwMDEwMjM4LDAuMjIzODU3NjI1IDEuMjIzOTYwMDEsMCAxLjUwMDEwMjM4LDAgQzEuNzc2MjQ0NzYsMCAyLjAwMDEwMjM4LDAuMjIzODU3NjI1IDIuMDAwMTAyMzgsMC41IEwyLjAwMDEwMjM4LDMuNzc0NjkzMTYgQzMuMzY4NjQzMjgsMi4wMzk0MDM0MyA1LjMyOTE3Nzk4LDEgNy41MDAxMDIzOCwxIEMxMS42NDIyNDQ4LDEgMTUuMDAwMTAyNCw0LjM1Nzg1NzYzIDE1LjAwMDEwMjQsOC41IEMxNS4wMDAxMDI0LDEyLjY0MjE0MjQgMTEuNjQyMjQ0OCwxNiA3LjUwMDEwMjM4LDE2IEM3LjIyMzk2MDAxLDE2IDcuMDAwMTAyMzgsMTUuNzc2MTQyNCA3LjAwMDEwMjM4LDE1LjUgQzcuMDAwMTAyMzgsMTUuMjIzODU3NiA3LjIyMzk2MDAxLDE1IDcuNTAwMTAyMzgsMTUgQzExLjA4OTk2LDE1IDE0LjAwMDEwMjQsMTIuMDg5ODU3NiAxNC4wMDAxMDI0LDguNSBDMTQuMDAwMTAyNCw0LjkxMDE0MjM3IDExLjA4OTk2LDIgNy41MDAxMDIzOCwyIEM1LjQxNTg0ODkyLDIgMy41NDU2NTQwMiwzLjEzMDY0MDcgMi4zNTUzMjgsNSBaIiBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9Im5vbnplcm8iIHRyYW5zZm9ybT0ibWF0cml4KC0xIDAgMCAxIDE2IDApIi8+PC9zdmc+'); --icon-Remove: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTYsNCBMNiwyLjUgQzYsMi4yMjM4NTc2MyA2LjIyMzg1NzYzLDIgNi41LDIgTDkuNSwyIEM5Ljc3NjE0MjM3LDIgMTAsMi4yMjM4NTc2MyAxMCwyLjUgTDEwLDQgTDEzLjUsNCBDMTMuNzc2MTQyNCw0IDE0LDQuMjIzODU3NjMgMTQsNC41IEMxNCw0Ljc3NjE0MjM3IDEzLjc3NjE0MjQsNSAxMy41LDUgTDIuNSw1IEMyLjIyMzg1NzYzLDUgMiw0Ljc3NjE0MjM3IDIsNC41IEMyLDQuMjIzODU3NjMgMi4yMjM4NTc2Myw0IDIuNSw0IEw2LDQgWiBNNyw0IEw5LDQgTDksMyBMNywzIEw3LDQgWiBNMTEsNi41IEMxMSw2LjIyMzg1NzYzIDExLjIyMzg1NzYsNiAxMS41LDYgQzExLjc3NjE0MjQsNiAxMiw2LjIyMzg1NzYzIDEyLDYuNSBMMTIsMTIuNSBDMTIsMTMuMzI4NDI3MSAxMS4zMjg0MjcxLDE0IDEwLjUsMTQgTDUuNSwxNCBDNC42NzE1NzI4OCwxNCA0LDEzLjMyODQyNzEgNCwxMi41IEw0LDYuNSBDNCw2LjIyMzg1NzYzIDQuMjIzODU3NjMsNiA0LjUsNiBDNC43NzYxNDIzNyw2IDUsNi4yMjM4NTc2MyA1LDYuNSBMNSwxMi41IEM1LDEyLjc3NjE0MjQgNS4yMjM4NTc2MywxMyA1LjUsMTMgTDEwLjUsMTMgQzEwLjc3NjE0MjQsMTMgMTEsMTIuNzc2MTQyNCAxMSwxMi41IEwxMSw2LjUgWiIgZmlsbD0iIzAwMCIgZmlsbC1ydWxlPSJub256ZXJvIi8+PC9zdmc+'); + --icon-RemoveBig: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PHBhdGggZD0iTSA1LjQ4MTQ3MTEsNCA1LjQ5ODQyMDMsMS4wNTkzMjIgQyA1LjUwMDAxMTksMC43ODMxODQyMiA1LjcyMTMyOTQsMC41NTkzMjIwMyA1Ljk5NjMwMTcsMC41NTkzMjIwMyBIIDEwLjE5ODkzIGMgMC4yNzQ5NzIsMCAwLjQ5OTQ3MiwwLjIyMzg2MjE5IDAuNDk3ODgxLDAuNDk5OTk5OTcgTCAxMC42Nzk4NjIsNCAxNS4zNTI5NzcsMy45ODMwNTA4IGMgMC4yNzQ5NzEsLTkuOTczZS00IDAuNDk3ODgxLDAuMjIzODU3NiAwLjQ5Nzg4MSwwLjUgMCwwLjI3NjE0MjQgLTAuMjIyOTA5LDAuNDk5NjgzNyAtMC40OTc4ODEsMC41IEwgMC42MTk3Nzc2OCw1IEMgMC4zNDQ4MDU1OCw1LjAwMDMxNjMgMC4xMjE4OTYzOSw0Ljc3NjE0MjQgMC4xMjE4OTYzOSw0LjUgYyAwLC0wLjI3NjE0MjQgMC4yMjI5MDksLTAuNSAwLjQ5Nzg4MTI5LC0wLjUgeiBNIDYuNDc3MjMzOCw0IEggOS42ODQwOTkgTCA5LjcwMTA0ODIsMS41NTkzMjIgSCA2LjQ5NDE4MyBaIG0gNS41Mjk4NDcyLDMuNjI2OTUxNCBjIC0zLjk4ZS00LC0wLjI3NjE0MjEgMC4yMjI5MDksLTAuNSAwLjQ5Nzg4MSwtMC41IDAuMjc0OTczLDAgMC40OTc0ODIsMC4yMjM4NTggMC40OTc4ODIsMC41IGwgMC4wMDkyLDYuMzQ2MjQwNiBjIDAuMDAxMiwwLjgyODQyNiAtMC42Njg3MjgsMS41IC0xLjQ5MzY0NCwxLjUgSCA0LjQ2MjQxMjYgYyAtMC44MjQ5MTY4LDAgLTEuNDkyNDQ5NiwtMC42NzE1NzQgLTEuNDkzNjQ0LC0xLjUgbCAtMC4wMDkxNSwtNi4zNDYyNDA2IGMgLTMuOTgxZS00LC0wLjI3NjE0MjEgMC4yMjI5MDkxLC0wLjUgMC40OTc4ODEzLC0wLjUgMC4yNzQ5NzIzLDAgMC40OTc0ODMzLDAuMjIzODU3OSAwLjQ5Nzg4MTQsMC41IGwgMC4wMDkxNSw2LjM0NjI0MDYgYyAzLjk4MWUtNCwwLjI3NjE0MSAwLjIyMjkwOTEsMC41IDAuNDk3ODgxMywwLjUgaCA3LjA1NTk0MDQgYyAwLjI3NDk3MiwwIDAuNDk4Mjc5LC0wLjIyMzg1OSAwLjQ5Nzg4MSwtMC41IHoiIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ZnPg=='); --icon-Repl: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTE0LjI5Mjg5MzIsMTIgTDEyLjE0NjQ0NjYsOS44NTM1NTMzOSBDMTEuOTUxMTg0NSw5LjY1ODI5MTI0IDExLjk1MTE4NDUsOS4zNDE3MDg3NiAxMi4xNDY0NDY2LDkuMTQ2NDQ2NjEgQzEyLjM0MTcwODgsOC45NTExODQ0NiAxMi42NTgyOTEyLDguOTUxMTg0NDYgMTIuODUzNTUzNCw5LjE0NjQ0NjYxIEwxNS44NTM1NTM0LDEyLjE0NjQ0NjYgQzE2LjA0ODgxNTUsMTIuMzQxNzA4OCAxNi4wNDg4MTU1LDEyLjY1ODI5MTIgMTUuODUzNTUzNCwxMi44NTM1NTM0IEwxMi44NTM1NTM0LDE1Ljg1MzU1MzQgQzEyLjY1ODI5MTIsMTYuMDQ4ODE1NSAxMi4zNDE3MDg4LDE2LjA0ODgxNTUgMTIuMTQ2NDQ2NiwxNS44NTM1NTM0IEMxMS45NTExODQ1LDE1LjY1ODI5MTIgMTEuOTUxMTg0NSwxNS4zNDE3MDg4IDEyLjE0NjQ0NjYsMTUuMTQ2NDQ2NiBMMTQuMjkyODkzMiwxMyBMMTIuMjk3LDEzIEMxMS4wMTE0MDE0LDEzIDkuNzg3MzEwNDYsMTIuNDUwMDg5NiA4LjkzMzM4NDQzLDExLjQ4OTI4MjEgTDcuOTA0Mzg0NDMsMTAuMzMyMjgyMSBDNy43MjA4NzAwOSwxMC4xMjU5Mzk5IDcuNzM5Mzc1Nyw5LjgwOTg5ODc4IDcuOTQ1NzE3ODgsOS42MjYzODQ0MyBDOC4xNTIwNjAwNiw5LjQ0Mjg3MDA5IDguNDY4MTAxMjIsOS40NjEzNzU3IDguNjUxNjE1NTcsOS42Njc3MTc4OCBMOS42ODA3Mjg4LDEwLjgyNDg0NTIgQzEwLjM0NTAyNTUsMTEuNTcyMjg4NCAxMS4yOTcxMDQsMTIgMTIuMjk3LDEyIEwxNC4yOTI4OTMyLDEyIFogTTAuNSw0IEMwLjIyMzg1NzYyNSw0IC01LjE5NTg0Mzc2ZS0xNCwzLjc3NjE0MjM3IC01LjE5NTg0Mzc2ZS0xNCwzLjUgQy01LjE5NTg0Mzc2ZS0xNCwzLjIyMzg1NzYzIDAuMjIzODU3NjI1LDMgMC41LDMgTDAuNzA0LDMgQzEuOTg5NTk4NTUsMyAzLjIxMzY4OTU0LDMuNTQ5OTEwNDIgNC4wNjc3NzU5NSw0LjUxMDg5ODMgTDUuMDk1Nzc1OTUsNS42Njc4OTgzIEM1LjI3OTE5MDY1LDUuODc0MzI5MDUgNS4yNjA1MzI0Niw2LjE5MDM2MTI0IDUuMDU0MTAxNyw2LjM3Mzc3NTk1IEM0Ljg0NzY3MDk1LDYuNTU3MTkwNjUgNC41MzE2Mzg3Niw2LjUzODUzMjQ2IDQuMzQ4MjI0MDUsNi4zMzIxMDE3IEwzLjMyMDI3MTIsNS4xNzUxNTQ3NiBDMi42NTU5NzQ1Miw0LjQyNzcxMTU5IDEuNzAzODk1OTksNCAwLjcwNCw0IEwwLjUsNCBaIE0xNC4yOTE4OTMyLDIuOTk5IEwxMi4xNDY0NDY2LDAuODUzNTUzMzkxIEMxMS45NTExODQ1LDAuNjU4MjkxMjQ1IDExLjk1MTE4NDUsMC4zNDE3MDg3NTUgMTIuMTQ2NDQ2NiwwLjE0NjQ0NjYwOSBDMTIuMzQxNzA4OCwtMC4wNDg4MTU1MzY1IDEyLjY1ODI5MTIsLTAuMDQ4ODE1NTM2NSAxMi44NTM1NTM0LDAuMTQ2NDQ2NjA5IEwxNS44MzQxOTg2LDMuMTI3MDkxODMgQzE1LjkzMTE4OSwzLjIxNDMwNTE3IDE1Ljk5Mzg4MDQsMy4zMzg5MTI3OCAxNS45OTk1NzU5LDMuNDc4MjE3NzQgQzE1Ljk5OTgzNzEsMy40ODUzNzc4MyAxNS45OTk5OTI4LDMuNDkyNDM5ODEgMTUuOTk5OTk5OCwzLjQ5OTUwMTkyIEMxNS45OTk5ODg0LDMuNTExMDYxMTMgMTUuOTk5NTg0OCwzLjUyMjUyODQ5IDE1Ljk5ODgwMTUsMy41MzM4OTE0MyBDMTUuOTkwOTk0NCwzLjY1MDMzMjU2IDE1Ljk0MjU1OTgsMy43NjQ1NDcwMiAxNS44NTM1NTM0LDMuODUzNTUzMzkgTDEyLjg1MzU1MzQsNi44NTM1NTMzOSBDMTIuNjU4MjkxMiw3LjA0ODgxNTU0IDEyLjM0MTcwODgsNy4wNDg4MTU1NCAxMi4xNDY0NDY2LDYuODUzNTUzMzkgQzExLjk1MTE4NDUsNi42NTgyOTEyNCAxMS45NTExODQ1LDYuMzQxNzA4NzYgMTIuMTQ2NDQ2Niw2LjE0NjQ0NjYxIEwxNC4yOTM4OTMyLDMuOTk5IEwxMi4yOTcsMy45OTkgQzExLjI5NzEwNCwzLjk5OSAxMC4zNDUwMjU1LDQuNDI2NzExNTkgOS42ODA3MTQ0NSw1LjE3NDE3MDkgTDQuMDY3NzI4OCwxMS40ODkxNTQ4IEMzLjIxMzY4OTU0LDEyLjQ1MDA4OTYgMS45ODk1OTg1NSwxMyAwLjcwNCwxMyBMMC41LDEzIEMwLjIyMzg1NzYyNSwxMyAtMi4yNjQ4NTQ5N2UtMTMsMTIuNzc2MTQyNCAtMi4yNjQ4NTQ5N2UtMTMsMTIuNSBDLTIuMjY0ODU0OTdlLTEzLDEyLjIyMzg1NzYgMC4yMjM4NTc2MjUsMTIgMC41LDEyIEwwLjcwNCwxMiBDMS43MDM4OTU5OSwxMiAyLjY1NTk3NDUyLDExLjU3MjI4ODQgMy4zMjAyODU1NSwxMC44MjQ4MjkxIEw4LjkzMzI3MTIsNC41MDk4NDUyNCBDOS43ODczMTA0NiwzLjU0ODkxMDQyIDExLjAxMTQwMTQsMi45OTkgMTIuMjk3LDIuOTk5IEwxNC4yOTE4OTMyLDIuOTk5IFoiIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ZnPg=='); --icon-ResizePanel: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHJlY3QgZmlsbD0iIzAwMCIgZmlsbC1ydWxlPSJub256ZXJvIiB4PSI0IiB5PSIyIiB3aWR0aD0iMiIgaGVpZ2h0PSIxMiIgcng9IjEiLz48L3N2Zz4='); --icon-Revert: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTMuMTIzMTcgMy4wMDMzTDMuMDg3MjggNi45MTEwM0w2LjgxMzM2IDUuNjY0MzYiIHN0cm9rZT0iIzE2QjM3OCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PHBhdGggZD0iTTEzLjYxNTggMTAuMDc5OUMxNC4wMzY0IDguNjg0NzggMTMuODg1NSA3LjE3OTc0IDEzLjE5NjUgNS44OTU4NiAxMi41MDc0IDQuNjExOTggMTEuMzM2NSAzLjY1NDQzIDkuOTQxMzcgMy4yMzM4NSA4LjU0NjI3IDIuODEzMjcgNy4wNDEyMyAyLjk2NDExIDUuNzU3MzUgMy42NTMyIDQuNDczNDcgNC4zNDIzIDMuNTE1OTIgNS41MTMxOCAzLjA5NTM0IDYuOTA4MjhNNi40Mjk0MyAxMy41ODc2QzUuODQ4OCAxMy4zNjgzIDUuMzA5NjIgMTMuMDUyIDQuODM0ODQgMTIuNjUyMk0xMC4wMzk4IDEzLjY4MTZDOS40OTk1MyAxMy44NTM0IDguOTM1OTUgMTMuOTQwNyA4LjM2OTAzIDEzLjk0MDJIOC4yNDMwNE0xMi44ODY4IDExLjU3MjhDMTIuNTQ4MiAxMi4wNjA4IDEyLjEzMzIgMTIuNDkxMiAxMS42NTc3IDEyLjg0NzNNMy42NjIxNSAxMS4yODA0QzMuMzUxNDggMTAuNzY2NiAzLjEyNzc2IDEwLjIwNTEgMyA5LjYxODQxIiBzdHJva2U9IiMxNkIzNzgiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjwvc3ZnPg=='); diff --git a/static/locales/en.client.json b/static/locales/en.client.json index 8d1117a4..755e0d9c 100644 --- a/static/locales/en.client.json +++ b/static/locales/en.client.json @@ -416,7 +416,7 @@ "Create Workspace": "Create Workspace", "Delete": "Delete", "Delete {{workspace}} and all included documents?": "Delete {{workspace}} and all included documents?", - "Examples & Templates": "Examples & Templates", + "Examples & Templates": "Templates", "Import Document": "Import Document", "Manage Users": "Manage Users", "Rename": "Rename", diff --git a/static/ui-icons/UI/Board.svg b/static/ui-icons/UI/Board.svg new file mode 100644 index 00000000..5aa1a46d --- /dev/null +++ b/static/ui-icons/UI/Board.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/ui-icons/UI/Bookmark.svg b/static/ui-icons/UI/Bookmark.svg new file mode 100644 index 00000000..d87a94e2 --- /dev/null +++ b/static/ui-icons/UI/Bookmark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/ui-icons/UI/GreenArrow.svg b/static/ui-icons/UI/GreenArrow.svg new file mode 100644 index 00000000..b9730e95 --- /dev/null +++ b/static/ui-icons/UI/GreenArrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/static/ui-icons/UI/RemoveBig.svg b/static/ui-icons/UI/RemoveBig.svg new file mode 100644 index 00000000..e11178dd --- /dev/null +++ b/static/ui-icons/UI/RemoveBig.svg @@ -0,0 +1,26 @@ + + + + Icons / UI / Remove + Created with Sketch. + + + + diff --git a/test/nbrowser/DocTutorial.ts b/test/nbrowser/DocTutorial.ts index 8d3ba803..2126d4e8 100644 --- a/test/nbrowser/DocTutorial.ts +++ b/test/nbrowser/DocTutorial.ts @@ -30,6 +30,19 @@ describe('DocTutorial', function () { session = await gu.session().anon.login(); }); + it('shows a tutorial card', async function() { + await session.loadRelPath('/'); + await gu.waitForDocMenuToLoad(); + await gu.skipWelcomeQuestions(); + + assert.isTrue(await driver.find('.test-tutorial-card-content').isDisplayed()); + // Can dismiss it. + await driver.find('.test-tutorial-card-close').click(); + assert.isFalse((await driver.findAll('.test-tutorial-card-content')).length > 0); + // When dismissed, we can see link in the menu. + assert.isTrue(await driver.find('.test-dm-basic-tutorial').isDisplayed()); + }); + it('redirects user to log in', async function() { await session.loadDoc(`/doc/${doc.id}`, false); await gu.checkLoginPage(); @@ -45,6 +58,34 @@ describe('DocTutorial', function () { afterEach(() => gu.checkForErrors()); + it('shows a tutorial card', async function() { + await session.loadRelPath('/'); + await gu.waitForDocMenuToLoad(); + await gu.skipWelcomeQuestions(); + + // Make sure we have clean start. + await driver.executeScript('resetSeenPopups();'); + await gu.waitForServer(); + await driver.navigate().refresh(); + await gu.waitForDocMenuToLoad(); + + // Make sure we see the card. + assert.isTrue(await driver.find('.test-tutorial-card-content').isDisplayed()); + + // And can dismiss it. + await driver.find('.test-tutorial-card-close').click(); + assert.isFalse((await driver.findAll('.test-tutorial-card-content')).length > 0); + + // When dismissed, we can see link in the menu. + assert.isTrue(await driver.find('.test-dm-basic-tutorial').isDisplayed()); + + // Prefs are preserved after reload. + await driver.navigate().refresh(); + await gu.waitForDocMenuToLoad(); + assert.isFalse((await driver.findAll('.test-tutorial-card-content')).length > 0); + assert.isTrue(await driver.find('.test-dm-basic-tutorial').isDisplayed()); + }); + it('creates a fork the first time the document is opened', async function() { await session.loadDoc(`/doc/${doc.id}`); await driver.wait(async () => { diff --git a/test/nbrowser/HomeIntro.ts b/test/nbrowser/HomeIntro.ts index 95f2084b..21733a99 100644 --- a/test/nbrowser/HomeIntro.ts +++ b/test/nbrowser/HomeIntro.ts @@ -248,7 +248,7 @@ describe('HomeIntro', function() { async function testSelectedExamplesPage() { // Click Examples & Templates in left panel. - await driver.findContent('.test-dm-templates-page', /Examples & Templates/).click(); + await driver.find('.test-dm-templates-page').click(); await gu.waitForDocMenuToLoad(); // Check Featured Templates are shown at the top of the page. diff --git a/test/nbrowser/gristUtils.ts b/test/nbrowser/gristUtils.ts index d7a80764..695aba3d 100644 --- a/test/nbrowser/gristUtils.ts +++ b/test/nbrowser/gristUtils.ts @@ -1977,6 +1977,7 @@ export class Session { freshAccount?: boolean, isFirstLogin?: boolean, showTips?: boolean, + skipTutorial?: boolean, // By default true retainExistingLogin?: boolean}) { // Optimize testing a little bit, so if we are already logged in as the expected // user on the expected org, and there are no options set, we can just continue. @@ -1994,6 +1995,11 @@ export class Session { } await server.simulateLogin(this.settings.name, this.settings.email, this.settings.orgDomain, {isFirstLogin: false, cacheCredentials: true, ...options}); + + if (options?.skipTutorial ?? true) { + await dismissTutorialCard(); + } + return this; } @@ -2033,10 +2039,7 @@ export class Session { if (wait === 'skipWelcomeQuestions') { // When waitForDocMenuToLoad() returns, welcome questions should also render, so that we // don't need to wait extra for them. - if (await driver.find('.test-welcome-questions').isPresent()) { - await driver.sendKeys(Key.ESCAPE); - assert.equal(await driver.find('.test-welcome-questions').isPresent(), false); - } + await skipWelcomeQuestions(); } } @@ -2802,13 +2805,35 @@ export async function dismissBehavioralPrompts() { } } +/** + * Dismisses any tutorial card that might be active. + */ +export async function dismissTutorialCard() { + // If there is something in our way, we can't do it. + if (await driver.find('.test-welcome-questions').isPresent()) { + return; + } + if (await driver.find('.test-tutorial-card-close').isPresent()) { + if (await driver.find('.test-tutorial-card-close').isDisplayed()) { + await driver.find('.test-tutorial-card-close').click(); + } + } +} + +/** + * Dismisses coaching call if needed. + */ +export async function dismissCoachingCall() { + const selector = '.test-coaching-call .test-popup-close-button'; + if ((await driver.findAll(selector)).length) { + await driver.find(selector).click(); + } +} + /** * Dismisses all card popups that are present. - * - * Optionally takes a `waitForServerTimeoutMs`, which may be null to skip waiting - * after closing each popup. */ - export async function dismissCardPopups(waitForServerTimeoutMs: number | null = 2000) { +export async function dismissCardPopups(waitForServerTimeoutMs: number | null = 2000) { let i = 0; const max = 10; @@ -2820,6 +2845,7 @@ export async function dismissBehavioralPrompts() { } } + /** * Confirms that anchor link was used for navigation. */ @@ -2920,6 +2946,17 @@ export function withComments() { }); } +/** + * Helper to scroll creator panel top or bottom. By default bottom. + */ +export function scrollPanel(top = false): WebElementPromise { + return new WebElementPromise(driver, + driver.executeScript((top: number) => { + document.getElementsByClassName('test-config-container')[0].scrollTop = top ? 0 : 10000; + }, top) + ); +} + /** * Helper to revert ACL changes. It first saves the current ACL data, and * then removes everything and adds it back. @@ -2968,6 +3005,13 @@ export async function setRangeFilterBound(minMax: 'min'|'max', value: string|{re } } +export async function skipWelcomeQuestions() { + if (await driver.find('.test-welcome-questions').isPresent()) { + await driver.sendKeys(Key.ESCAPE); + assert.equal(await driver.find('.test-welcome-questions').isPresent(), false); + } +} + } // end of namespace gristUtils stackWrapOwnMethods(gristUtils);