diff --git a/packages/background-tips/README.md b/packages/background-tips/README.md index 5a3872aa7c..0e117f4e10 100644 --- a/packages/background-tips/README.md +++ b/packages/background-tips/README.md @@ -3,3 +3,21 @@ Displays tips about Pulsar in the background when there are no open editors.  + +### Contributing tips + +Packages can contribute tips by adding a `backgroundTips` array to their `package.json`. Each entry is a string displayed as-is, with optional `{command}` placeholders that are replaced by the current keybinding for that command. + +```json +"backgroundTips": [ + "You can toggle the Tree View with {atom-workspace>tree-view:toggle}", + "You can open any file quickly using {atom-workspace>fuzzy-finder:toggle-file-finder}" +] +``` + +The placeholder syntax is `{command}` or `{scope>command}`: + +- `{command}` resolves the keybinding using the current platform. +- `{scope>command}` resolves the keybinding for the given CSS selector scope (e.g. `atom-workspace`, `atom-text-editor`, `body`). + +If the command in a placeholder has no keybinding defined, the tip is skipped entirely. diff --git a/packages/background-tips/lib/background-tips-view.js b/packages/background-tips/lib/background-tips-view.js index d6efce494b..3263e46650 100644 --- a/packages/background-tips/lib/background-tips-view.js +++ b/packages/background-tips/lib/background-tips-view.js @@ -1,156 +1,187 @@ -const _ = require('underscore-plus') -const {CompositeDisposable, Disposable} = require('atom') -const Tips = require('./tips') +const _ = require('underscore-plus'); +const { CompositeDisposable } = require('atom'); const TEMPLATE = `\
\ -` - -module.exports = -class BackgroundTipsElement { - constructor () { - this.element = document.createElement('background-tips') - this.index = -1 - this.workspaceCenter = atom.workspace.getCenter() - - this.startDelay = 1000 - this.displayDuration = 10000 - this.fadeDuration = 300 - - this.disposables = new CompositeDisposable() - - const visibilityCallback = () => this.updateVisibility() - - this.disposables.add(this.workspaceCenter.onDidAddPane(visibilityCallback)) - this.disposables.add(this.workspaceCenter.onDidDestroyPane(visibilityCallback)) - this.disposables.add(this.workspaceCenter.onDidChangeActivePaneItem(visibilityCallback)) - - atom.getCurrentWindow().on('blur', visibilityCallback) - atom.getCurrentWindow().on('focus', visibilityCallback) - - this.disposables.add(new Disposable(() => atom.getCurrentWindow().removeListener('blur', visibilityCallback))) - this.disposables.add(new Disposable(() => atom.getCurrentWindow().removeListener('focus', visibilityCallback))) - - this.startTimeout = setTimeout(() => this.start(), this.startDelay) +`; + +module.exports = class BackgroundTipsElement { + constructor() { + this.element = document.createElement('background-tips'); + this.index = -1; + this.workspaceCenter = atom.workspace.getCenter(); + this.startDelay = 1000; + this.fadeDuration = 300; + this.tips = []; + this.tipSources = []; + this.started = false; + this.disposables = new CompositeDisposable(); + const visibilityCallback = () => this.updateVisibility(); + this.disposables.add( + this.workspaceCenter.onDidAddPane(visibilityCallback), + this.workspaceCenter.onDidDestroyPane(visibilityCallback), + this.workspaceCenter.onDidChangeActivePaneItem(visibilityCallback), + atom.config.observe('background-tips.displayDuration', (value) => { + this.displayDuration = value * 1000; + }), + ); + this.startTimeout = setTimeout(() => { this.started = true; this.start(); }, this.startDelay); } - destroy () { - this.stop() - this.disposables.dispose() + destroy() { + this.stop(); + this.disposables.dispose(); } - attach () { - this.element.innerHTML = TEMPLATE - this.message = this.element.querySelector('.message') - - const paneView = atom.views.getView(this.workspaceCenter.getActivePane()) - const itemViews = paneView.querySelector('.item-views') - let top = 0 + attach() { + this.element.innerHTML = TEMPLATE; + this.message = this.element.querySelector('.message'); + const paneView = atom.views.getView(this.workspaceCenter.getActivePane()); + const itemViews = paneView.querySelector('.item-views'); + let top = 0; if (itemViews && itemViews.offsetTop) { - top = itemViews.offsetTop + top = itemViews.offsetTop; } - - this.element.style.top = top + 'px' - paneView.appendChild(this.element) + this.element.style.top = top + 'px'; + paneView.appendChild(this.element); } - detach () { - this.element.remove() - } - - updateVisibility () { + updateVisibility() { if (this.shouldBeAttached()) { - this.start() + this.start(); } else { - this.stop() + this.stop(); } } - shouldBeAttached () { - return this.workspaceCenter.getPanes().length === 1 && - this.workspaceCenter.getActivePaneItem() == null && - atom.getCurrentWindow().isFocused() + shouldBeAttached() { + return ( + this.workspaceCenter.getPanes().length === 1 && + this.workspaceCenter.getActivePaneItem() == null + ); } - start () { - if (!this.shouldBeAttached() || this.interval != null) return - this.renderTips() - this.randomizeIndex() - this.attach() - this.showNextTip() - this.interval = setInterval(() => this.showNextTip(), this.displayDuration) + start() { + if (!this.shouldBeAttached() || this.interval != null) return; + if (this.tips.length === 0) return; + this.randomizeIndex(); + this.attach(); + this.showNextTip(); + this.interval = setInterval(() => this.showNextTip(), this.displayDuration); } - stop () { - this.element.remove() + stop() { + this.element.remove(); if (this.interval != null) { - clearInterval(this.interval) + clearInterval(this.interval); } - - clearTimeout(this.startTimeout) - clearTimeout(this.nextTipTimeout) - this.interval = null + clearTimeout(this.startTimeout); + clearTimeout(this.nextTipTimeout); + this.interval = null; } - randomizeIndex () { - const len = Tips.length - this.index = Math.round(Math.random() * len) % len + randomizeIndex() { + const len = this.tips.length; + this.index = len > 0 ? Math.round(Math.random() * len) % len : 0; } - showNextTip () { - this.index = ++this.index % Tips.length - this.message.classList.remove('fade-in') + showNextTip() { + if (this.tips.length === 0) return; + let html = null; + for (let i = 0; i < this.tips.length; i++) { + this.index = (this.index + 1) % this.tips.length; + if (atom.packages.isPackageDisabled(this.tipSources[this.index])) continue; + html = this.renderTip(this.tips[this.index]); + if (html !== null) break; + this.log(`Skipping tip (missing keybinding): "${this.tips[this.index]}"`); + } + if (html === null) { + this.stop(); + return; + } + this.message.classList.remove('fade-in'); this.nextTipTimeout = setTimeout(() => { - this.message.innerHTML = Tips[this.index] - this.message.classList.add('fade-in') - }, this.fadeDuration) + this.log(`Displaying tip [${this.index + 1}/${this.tips.length}]: "${this.tips[this.index]}"`); + this.message.innerHTML = html; + this.message.classList.add('fade-in'); + }, this.fadeDuration); } - renderTips () { - if (this.tipsRendered) return - for (let i = 0; i < Tips.length; i++) { - const tip = Tips[i] - Tips[i] = this.renderTip(tip) + addPackageTips(pkg) { + const raw = pkg.metadata.backgroundTips; + if (!Array.isArray(raw) || raw.length === 0) return; + for (const tip of raw) { + this.tips.push(tip); + this.tipSources.push(pkg.name); } - this.tipsRendered = true + this.log(`Package "${pkg.name}" added ${raw.length} tip(s), total: ${this.tips.length}`); + if (this.started && this.interval == null) this.start(); } - renderTip (str) { - str = str.replace(/\{(.+)\}/g, (match, command) => { - let binding, scope - const scopeAndCommand = command.split('>') - if (scopeAndCommand.length > 1) { - [scope, command] = scopeAndCommand + removePackageTips(pkg) { + const keep = []; + const keepSources = []; + for (let i = 0; i < this.tips.length; i++) { + if (this.tipSources[i] !== pkg.name) { + keep.push(this.tips[i]); + keepSources.push(this.tipSources[i]); } - const bindings = atom.keymaps.findKeyBindings({command: command.trim()}) + } + const removed = this.tips.length - keep.length; + if (removed === 0) return; + this.log(`Package "${pkg.name}" removed ${removed} tip(s), total: ${keep.length}`); + this.tips = keep; + this.tipSources = keepSources; + if (this.interval != null) { + if (this.tips.length === 0) { + this.stop(); + } else { + this.index = Math.min(this.index, this.tips.length - 1); + } + } + } + renderTip(str) { + let missing = false; + const html = str.replace(/\{(.+)\}/g, (match, command) => { + let binding, scope; + const scopeAndCommand = command.split('>'); + if (scopeAndCommand.length > 1) { + [scope, command] = scopeAndCommand; + } + const bindings = atom.keymaps.findKeyBindings({ command: command.trim() }); if (scope) { for (binding of bindings) { - if (binding.selector === scope) break + if (binding.selector === scope) break; } } else { - binding = this.getKeyBindingForCurrentPlatform(bindings) + binding = this.getKeyBindingForCurrentPlatform(bindings); } - if (binding && binding.keystrokes) { - const keystrokeLabel = _.humanizeKeystroke(binding.keystrokes).replace(/\s+/g, ' ') - return `${keystrokeLabel}` + const keystrokeLabel = _.humanizeKeystroke(binding.keystrokes).replace(/\s+/g, ' '); + return `${keystrokeLabel}`; } else { - return command + missing = true; + return ''; } - }) - return str + }); + return missing ? null : html; } - getKeyBindingForCurrentPlatform (bindings) { - if (!bindings || !bindings.length) return - for (let binding of bindings) { + getKeyBindingForCurrentPlatform(bindings) { + if (!bindings || !bindings.length) return; + for (const binding of bindings) { if (binding.selector.indexOf(process.platform) !== -1) { - return binding + return binding; } } - return bindings[0] + return bindings[0]; + } + + log(...args) { + if (atom.config.get('background-tips.debug')) { + console.log('[background-tips]', ...args); + } } -} +}; diff --git a/packages/background-tips/lib/background-tips.js b/packages/background-tips/lib/background-tips.js index 3c827be7e2..728ba27361 100644 --- a/packages/background-tips/lib/background-tips.js +++ b/packages/background-tips/lib/background-tips.js @@ -1,11 +1,25 @@ -const BackgroundTipsView = require('./background-tips-view') +const { CompositeDisposable } = require("atom"); +const BackgroundTipsView = require("./background-tips-view"); module.exports = { - activate () { - this.backgroundTipsView = new BackgroundTipsView() + activate() { + this.backgroundTipsView = new BackgroundTipsView(); + this.disposables = new CompositeDisposable(); + for (let pkg of atom.packages.getLoadedPackages()) { + this.backgroundTipsView.addPackageTips(pkg); + } + this.disposables.add( + atom.packages.onDidLoadPackage((pkg) => { + this.backgroundTipsView.addPackageTips(pkg); + }), + atom.packages.onDidUnloadPackage((pkg) => { + this.backgroundTipsView.removePackageTips(pkg); + }), + ) }, - deactivate () { - this.backgroundTipsView.destroy() - } -} + deactivate() { + this.disposables.dispose(); + this.backgroundTipsView.destroy(); + }, +}; diff --git a/packages/background-tips/lib/tips.js b/packages/background-tips/lib/tips.js deleted file mode 100644 index fa70dc48a7..0000000000 --- a/packages/background-tips/lib/tips.js +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = [ - 'Close panels like find and replace with {body>core:cancel}', - `Everything ${atom.branding.name} can do is in the Command Palette. See it by using {command-palette:toggle}`, - 'You can quickly open files with the Fuzzy Finder. Try it by using {fuzzy-finder:toggle-file-finder}', - 'You can toggle the Tree View with {tree-view:toggle}', - 'You can focus the Tree View with {tree-view:toggle-focus}', - 'You can toggle the Git tab with {github:toggle-git-tab}', - 'You can focus the Git tab with {github:toggle-git-tab-focus}', - 'You can toggle the GitHub tab with {github:toggle-github-tab}', - 'You can focus the GitHub tab with {github:toggle-github-tab-focus}', - 'You can split a pane with {pane:split-right-and-copy-active-item}', - 'You can jump to a method in the editor using {symbols-view:toggle-file-symbols}', - 'You can install packages and themes from the Settings View {settings-view:open}' -] diff --git a/packages/background-tips/package.json b/packages/background-tips/package.json index d3b7bb1145..511aada7c3 100644 --- a/packages/background-tips/package.json +++ b/packages/background-tips/package.json @@ -9,6 +9,27 @@ "engines": { "atom": ">0.42.0" }, + "backgroundTips": [ + "You can split your editor into multiple panes with {pane:split-right-and-copy-active-item}", + "You can move a line of text up with {atom-text-editor>editor:move-line-up}", + "You can move a line of text down with {atom-text-editor>editor:move-line-down}", + "You can toggle line comments with {atom-text-editor>editor:toggle-line-comments}" + ], + "configSchema": { + "displayDuration": { + "type": "integer", + "default": 10, + "minimum": 1, + "description": "How long each tip is displayed, in seconds.", + "order": 1 + }, + "debug": { + "type": "boolean", + "default": false, + "description": "Log debug information about tip collection and display to the console.", + "order": 2 + } + }, "dependencies": { "underscore-plus": "1.x" } diff --git a/packages/background-tips/spec/async-spec-helpers.js b/packages/background-tips/spec/async-spec-helpers.js index 73002c049a..c989e292c8 100644 --- a/packages/background-tips/spec/async-spec-helpers.js +++ b/packages/background-tips/spec/async-spec-helpers.js @@ -1,103 +1,106 @@ /** @babel */ -export function beforeEach (fn) { +export function beforeEach(fn) { global.beforeEach(function () { - const result = fn() + const result = fn(); if (result instanceof Promise) { - waitsForPromise(() => result) + waitsForPromise(() => result); } - }) + }); } -export function afterEach (fn) { +export function afterEach(fn) { global.afterEach(function () { - const result = fn() + const result = fn(); if (result instanceof Promise) { - waitsForPromise(() => result) + waitsForPromise(() => result); } - }) + }); } -['it', 'fit', 'ffit', 'fffit'].forEach(function (name) { +["it", "fit", "ffit", "fffit"].forEach(function (name) { module.exports[name] = function (description, fn) { if (fn === undefined) { - global[name](description) - return + global[name](description); + return; } global[name](description, function () { - const result = fn() + const result = fn(); if (result instanceof Promise) { - waitsForPromise(() => result) + waitsForPromise(() => result); } - }) - } -}) + }); + }; +}); -export async function conditionPromise (condition, description = 'anonymous condition') { - const startTime = Date.now() +export async function conditionPromise( + condition, + description = "anonymous condition" +) { + const startTime = Date.now(); while (true) { - await timeoutPromise(100) + await timeoutPromise(100); if (await condition()) { - return + return; } if (Date.now() - startTime > 5000) { - throw new Error('Timed out waiting on ' + description) + throw new Error("Timed out waiting on " + description); } } } -export function timeoutPromise (timeout) { +export function timeoutPromise(timeout) { return new Promise(function (resolve) { - global.setTimeout(resolve, timeout) - }) + global.setTimeout(resolve, timeout); + }); } -function waitsForPromise (fn) { - const promise = fn() - global.waitsFor('spec promise to resolve', function (done) { +function waitsForPromise(fn) { + const promise = fn(); + global.waitsFor("spec promise to resolve", function (done) { promise.then(done, function (error) { - jasmine.getEnv().currentSpec.fail(error) - done() - }) - }) + jasmine.getEnv().currentSpec.fail(error); + done(); + }); + }); } -export function emitterEventPromise (emitter, event, timeout = 15000) { +export function emitterEventPromise(emitter, event, timeout = 15000) { return new Promise((resolve, reject) => { const timeoutHandle = setTimeout(() => { - reject(new Error(`Timed out waiting for '${event}' event`)) - }, timeout) + reject(new Error(`Timed out waiting for '${event}' event`)); + }, timeout); emitter.once(event, () => { - clearTimeout(timeoutHandle) - resolve() - }) - }) + clearTimeout(timeoutHandle); + resolve(); + }); + }); } -export function promisify (original) { +export function promisify(original) { return function (...args) { return new Promise((resolve, reject) => { args.push((err, ...results) => { if (err) { - reject(err) + reject(err); } else { - resolve(...results) + resolve(...results); } - }) + }); - return original(...args) - }) - } + return original(...args); + }); + }; } -export function promisifySome (obj, fnNames) { - const result = {} +export function promisifySome(obj, fnNames) { + const result = {}; for (const fnName of fnNames) { - result[fnName] = promisify(obj[fnName]) + result[fnName] = promisify(obj[fnName]); } - return result + return result; } diff --git a/packages/background-tips/spec/background-tips-spec.js b/packages/background-tips/spec/background-tips-spec.js index 86a3c78bfb..7eba5ca21f 100644 --- a/packages/background-tips/spec/background-tips-spec.js +++ b/packages/background-tips/spec/background-tips-spec.js @@ -1,142 +1,115 @@ -const {it, fit, ffit, afterEach, beforeEach, emitterEventPromise} = require('./async-spec-helpers') // eslint-disable-line no-unused-vars +const { + it, + fit, + ffit, + afterEach, + beforeEach, +} = require("./async-spec-helpers"); -describe('BackgroundTips', () => { - let workspaceElement +describe("BackgroundTips", () => { + let workspaceElement; const activatePackage = async () => { - const {mainModule} = await atom.packages.activatePackage('background-tips') - return mainModule.backgroundTipsView - } + const { mainModule } = await atom.packages.activatePackage( + "background-tips" + ); + return mainModule.backgroundTipsView; + }; beforeEach(() => { - workspaceElement = atom.views.getView(atom.workspace) - jasmine.attachToDOM(workspaceElement) - jasmine.useMockClock() - spyOn(atom.getCurrentWindow(), 'isFocused').andReturn(true) - }) + workspaceElement = atom.views.getView(atom.workspace); + jasmine.attachToDOM(workspaceElement); + jasmine.useMockClock(); + }); - describe('when the package is activated when there is only one pane', () => { + describe("when the package is activated when there is only one pane", () => { beforeEach(() => { - expect(atom.workspace.getCenter().getPanes().length).toBe(1) - }) - - describe('when the pane is empty', () => { - it('attaches the view after a delay', async () => { - expect(atom.workspace.getActivePane().getItems().length).toBe(0) - - const backgroundTipsView = await activatePackage() - expect(backgroundTipsView.element.parentNode).toBeFalsy() - advanceClock(backgroundTipsView.startDelay + 1) - expect(backgroundTipsView.element.parentNode).toBeTruthy() - }) - }) - - describe('when the pane is not empty', () => { - it('does not attach the view', async () => { - await atom.workspace.open() - - const backgroundTipsView = await activatePackage() - advanceClock(backgroundTipsView.startDelay + 1) - expect(backgroundTipsView.element.parentNode).toBeFalsy() - }) - }) - - describe('when a second pane is created', () => { - it('detaches the view', async () => { - const backgroundTipsView = await activatePackage() - advanceClock(backgroundTipsView.startDelay + 1) - expect(backgroundTipsView.element.parentNode).toBeTruthy() - - atom.workspace.getActivePane().splitRight() - expect(backgroundTipsView.element.parentNode).toBeFalsy() - }) - }) - }) - - describe('when the package is activated when there are multiple panes', () => { + expect(atom.workspace.getCenter().getPanes().length).toBe(1); + }); + + describe("when the pane is empty", () => { + it("attaches the view after a delay", async () => { + expect(atom.workspace.getActivePane().getItems().length).toBe(0); + + const backgroundTipsView = await activatePackage(); + expect(backgroundTipsView.element.parentNode).toBeFalsy(); + advanceClock(backgroundTipsView.startDelay + 1); + expect(backgroundTipsView.element.parentNode).toBeTruthy(); + }); + }); + + describe("when the pane is not empty", () => { + it("does not attach the view", async () => { + await atom.workspace.open(); + + const backgroundTipsView = await activatePackage(); + advanceClock(backgroundTipsView.startDelay + 1); + expect(backgroundTipsView.element.parentNode).toBeFalsy(); + }); + }); + + describe("when a second pane is created", () => { + it("detaches the view", async () => { + const backgroundTipsView = await activatePackage(); + advanceClock(backgroundTipsView.startDelay + 1); + expect(backgroundTipsView.element.parentNode).toBeTruthy(); + + atom.workspace.getActivePane().splitRight(); + expect(backgroundTipsView.element.parentNode).toBeFalsy(); + }); + }); + }); + + describe("when the package is activated when there are multiple panes", () => { beforeEach(() => { - atom.workspace.getActivePane().splitRight() - expect(atom.workspace.getCenter().getPanes().length).toBe(2) - }) - - it('does not attach the view', async () => { - const backgroundTipsView = await activatePackage() - advanceClock(backgroundTipsView.startDelay + 1) - expect(backgroundTipsView.element.parentNode).toBeFalsy() - }) - - describe('when all but the last pane is destroyed', () => { - it('attaches the view', async () => { - const backgroundTipsView = await activatePackage() - atom.workspace.getActivePane().destroy() - advanceClock(backgroundTipsView.startDelay + 1) - expect(backgroundTipsView.element.parentNode).toBeTruthy() - - atom.workspace.getActivePane().splitRight() - expect(backgroundTipsView.element.parentNode).toBeFalsy() - - atom.workspace.getActivePane().destroy() - expect(backgroundTipsView.element.parentNode).toBeTruthy() - }) - }) - }) - - describe('when the view is attached', () => { - let backgroundTipsView + atom.workspace.getActivePane().splitRight(); + expect(atom.workspace.getCenter().getPanes().length).toBe(2); + }); + + it("does not attach the view", async () => { + const backgroundTipsView = await activatePackage(); + advanceClock(backgroundTipsView.startDelay + 1); + expect(backgroundTipsView.element.parentNode).toBeFalsy(); + }); + + describe("when all but the last pane is destroyed", () => { + it("attaches the view", async () => { + const backgroundTipsView = await activatePackage(); + atom.workspace.getActivePane().destroy(); + advanceClock(backgroundTipsView.startDelay + 1); + expect(backgroundTipsView.element.parentNode).toBeTruthy(); + + atom.workspace.getActivePane().splitRight(); + expect(backgroundTipsView.element.parentNode).toBeFalsy(); + + atom.workspace.getActivePane().destroy(); + expect(backgroundTipsView.element.parentNode).toBeTruthy(); + }); + }); + }); + + describe("when the view is attached", () => { + let backgroundTipsView; beforeEach(async () => { - expect(atom.workspace.getCenter().getPanes().length).toBe(1) - - backgroundTipsView = await activatePackage() - advanceClock(backgroundTipsView.startDelay) - advanceClock(backgroundTipsView.fadeDuration) - }) - - it('has text in the message', () => { - expect(backgroundTipsView.element.parentNode).toBeTruthy() - expect(backgroundTipsView.message.textContent).toBeTruthy() - }) - - it('changes text in the message', async () => { - const oldText = backgroundTipsView.message.textContent - advanceClock(backgroundTipsView.displayDuration) - advanceClock(backgroundTipsView.fadeDuration) - expect(backgroundTipsView.message.textContent).not.toEqual(oldText) - }) - }) - - describe('when Atom is not focused but all other requirements are satisfied', () => { - beforeEach(() => { - jasmine.unspy(atom.getCurrentWindow(), 'isFocused') - spyOn(atom.getCurrentWindow(), 'isFocused').andReturn(false) - }) - - it('does not display the background tips', async () => { - expect(atom.workspace.getActivePane().getItems().length).toBe(0) - - const backgroundTipsView = await activatePackage() - expect(backgroundTipsView.element.parentNode).toBeFalsy() - advanceClock(backgroundTipsView.startDelay + 1) - expect(backgroundTipsView.element.parentNode).toBeFalsy() - }) - - it('reactivates the background tips if the focus event is received', async () => { - expect(atom.workspace.getActivePane().getItems().length).toBe(0) - - const backgroundTipsView = await activatePackage() - advanceClock(backgroundTipsView.startDelay + 1) - expect(backgroundTipsView.element.parentNode).toBeFalsy() - - jasmine.unspy(atom.getCurrentWindow(), 'isFocused') - spyOn(atom.getCurrentWindow(), 'isFocused').andReturn(true) - - const focusEvent = emitterEventPromise(atom.getCurrentWindow(), 'focus') - atom.getCurrentWindow().emit('focus') // Manually emit to prevent actually blurring + refocusing the window - - await focusEvent - - advanceClock(backgroundTipsView.startDelay + 1) - expect(backgroundTipsView.element.parentNode).toBeTruthy() - }) - }) -}) + expect(atom.workspace.getCenter().getPanes().length).toBe(1); + + backgroundTipsView = await activatePackage(); + advanceClock(backgroundTipsView.startDelay); + advanceClock(backgroundTipsView.fadeDuration); + }); + + it("has text in the message", () => { + expect(backgroundTipsView.element.parentNode).toBeTruthy(); + expect(backgroundTipsView.message.textContent).toBeTruthy(); + }); + + it("changes text in the message", async () => { + const oldText = backgroundTipsView.message.textContent; + advanceClock(backgroundTipsView.displayDuration); + advanceClock(backgroundTipsView.fadeDuration); + expect(backgroundTipsView.message.textContent).not.toEqual(oldText); + }); + }); + +}); diff --git a/packages/background-tips/styles/background-tips.less b/packages/background-tips/styles/background-tips.less index d5a7781372..52d33029d6 100644 --- a/packages/background-tips/styles/background-tips.less +++ b/packages/background-tips/styles/background-tips.less @@ -1,7 +1,3 @@ -// The ui-variables file is provided by base themes provided by Pulsar. -// -// See https://github.com/pulsar-edit/pulsar/blob/master/packages/atom-dark-ui/styles/ui-variables.less -// for a full listing of what's available. @import "ui-variables"; background-tips { diff --git a/packages/command-palette/package.json b/packages/command-palette/package.json index f791b96b8e..4ec7b50ff8 100644 --- a/packages/command-palette/package.json +++ b/packages/command-palette/package.json @@ -24,6 +24,9 @@ "semver": "^5.4.1", "sinon": "^3.2.1" }, + "backgroundTips": [ + "Everything Pulsar can do is in the Command Palette. See it by using {atom-workspace>command-palette:toggle}" + ], "configSchema": { "preserveLastSearch": { "type": "boolean", diff --git a/packages/find-and-replace/package.json b/packages/find-and-replace/package.json index 1c06bab840..0697b395a0 100644 --- a/packages/find-and-replace/package.json +++ b/packages/find-and-replace/package.json @@ -40,6 +40,9 @@ "devDependencies": { "dedent": "^0.6.0" }, + "backgroundTips": [ + "Dismiss panels like Find and Replace with {body>core:cancel}" + ], "consumedServices": { "atom.file-icons": { "versions": { diff --git a/packages/fuzzy-finder/package.json b/packages/fuzzy-finder/package.json index f255a35d15..183daa670c 100644 --- a/packages/fuzzy-finder/package.json +++ b/packages/fuzzy-finder/package.json @@ -43,6 +43,9 @@ } } }, + "backgroundTips": [ + "You can quickly open files with the Fuzzy Finder using {atom-workspace>fuzzy-finder:toggle-file-finder}" + ], "configSchema": { "ignoredNames": { "type": "array", diff --git a/packages/settings-view/package.json b/packages/settings-view/package.json index 6826a02fc3..00995c04ed 100644 --- a/packages/settings-view/package.json +++ b/packages/settings-view/package.json @@ -67,6 +67,9 @@ } } }, + "backgroundTips": [ + "You can install packages, themes, and customize your editor in Settings with {atom-workspace>settings-view:open}" + ], "deserializers": { "SettingsView": "createSettingsView" } diff --git a/packages/symbols-view/package.json b/packages/symbols-view/package.json index 8e10033008..5708bde1a1 100644 --- a/packages/symbols-view/package.json +++ b/packages/symbols-view/package.json @@ -70,6 +70,9 @@ } } }, + "backgroundTips": [ + "You can jump to any function or symbol in the current file using {atom-workspace>symbols-view:toggle-file-symbols}" + ], "providedServices": { "hyperclick": { "versions": { diff --git a/packages/tree-view/package.json b/packages/tree-view/package.json index faef314eff..5253beb851 100644 --- a/packages/tree-view/package.json +++ b/packages/tree-view/package.json @@ -31,6 +31,10 @@ } } }, + "backgroundTips": [ + "You can toggle the Tree View with {atom-workspace>tree-view:toggle}", + "You can focus the Tree View with {atom-workspace>tree-view:toggle-focus}" + ], "providedServices": { "tree-view": { "description": "A tree-like view of directories and files",