Improve manual execution instructions
- Simplify alternatives to run the script.
- Change style of code parts to be easier to the eye:
- Use the same font size as other texts in body.
- Add vertical padding.
- Align the contents (the code, copy button and dollar sign) in the
middle.
- Align information icon to center of context next to it.
- Fix minor typos and punctations.
- Refactor instruction list to be more generic to able to be used by
other operating systems.
- Make dialogs scrollable so instruction list on smaller screens can be
read until the end.
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<span class="code-wrapper">
|
||||
<span class="dollar">$</span>
|
||||
<code><slot></slot></code>
|
||||
<font-awesome-icon
|
||||
class="copy-button"
|
||||
:icon="['fas', 'copy']"
|
||||
@click="copyCode"
|
||||
v-tooltip.top-center="'Copy'"
|
||||
/>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
import { Clipboard } from '@/infrastructure/Clipboard';
|
||||
|
||||
@Component
|
||||
export default class Code extends Vue {
|
||||
public copyCode(): void {
|
||||
const code = this.$slots.default[0].text;
|
||||
Clipboard.copyText(code);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "@/presentation/assets/styles/main" as *;
|
||||
|
||||
.code-wrapper {
|
||||
display:flex;
|
||||
white-space: nowrap;
|
||||
justify-content: space-between;
|
||||
font-family: $font-normal;
|
||||
background-color: $color-primary-darker;
|
||||
color: $color-on-primary;
|
||||
align-items: center;
|
||||
padding: 0.2rem;
|
||||
.dollar {
|
||||
margin-right: 0.5rem;
|
||||
font-size: 0.8rem;
|
||||
user-select: none;
|
||||
}
|
||||
.copy-button {
|
||||
margin-left: 1rem;
|
||||
@include clickable;
|
||||
@include hover-or-touch {
|
||||
color: $color-primary;
|
||||
}
|
||||
}
|
||||
code {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,30 @@
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
import { IInstructionListData, IInstructionListStep } from '../InstructionListData';
|
||||
|
||||
export interface IInstructionsBuilderData {
|
||||
readonly fileName: string;
|
||||
}
|
||||
|
||||
export type InstructionStepBuilderType = (data: IInstructionsBuilderData) => IInstructionListStep;
|
||||
|
||||
export class InstructionsBuilder {
|
||||
private readonly stepBuilders = new Array<InstructionStepBuilderType>();
|
||||
|
||||
constructor(private readonly os: OperatingSystem) {
|
||||
|
||||
}
|
||||
|
||||
public withStep(stepBuilder: InstructionStepBuilderType) {
|
||||
if (!stepBuilder) { throw new Error('missing stepBuilder'); }
|
||||
this.stepBuilders.push(stepBuilder);
|
||||
return this;
|
||||
}
|
||||
|
||||
public build(data: IInstructionsBuilderData): IInstructionListData {
|
||||
if (!data) { throw new Error('missing data'); }
|
||||
return {
|
||||
operatingSystem: this.os,
|
||||
steps: this.stepBuilders.map((stepBuilder) => stepBuilder(data)),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
import { InstructionsBuilder } from './InstructionsBuilder';
|
||||
|
||||
export class MacOsInstructionsBuilder extends InstructionsBuilder {
|
||||
constructor() {
|
||||
super(OperatingSystem.macOS);
|
||||
super
|
||||
.withStep(() => ({
|
||||
action: {
|
||||
instruction: 'Download the file.',
|
||||
details: 'You should have already been prompted to save the script file.'
|
||||
+ '<br/>If this was not the case or you did not save the script when prompted,'
|
||||
+ '<br/>please try to download your script file again.'
|
||||
,
|
||||
},
|
||||
}))
|
||||
.withStep(() => ({
|
||||
action: {
|
||||
instruction: 'Open terminal.',
|
||||
details: 'Type Terminal into Spotlight or open it from the Applications -> Utilities folder.',
|
||||
},
|
||||
}))
|
||||
.withStep(() => ({
|
||||
action: {
|
||||
instruction: 'Navigate to the folder where you downloaded the file e.g.:',
|
||||
},
|
||||
code: {
|
||||
instruction: 'cd ~/Downloads',
|
||||
details: 'Press on <code>enter/return</code> key after running the command.'
|
||||
+ '<br/>If the file is not downloaded on Downloads folder,'
|
||||
+ '<br/>change <code>Downloads</code> to path where the file is downloaded.'
|
||||
+ '<br/>'
|
||||
+ '<br/>This command means:'
|
||||
+ '<ul>'
|
||||
+ '<li><code>cd</code> will change the current folder.</li>'
|
||||
+ '<li><code>~</code> is the user home directory.</li>'
|
||||
+ '</ul>',
|
||||
},
|
||||
}))
|
||||
.withStep((data) => ({
|
||||
action: {
|
||||
instruction: 'Give the file execute permissions:',
|
||||
},
|
||||
code: {
|
||||
instruction: `chmod +x ${data.fileName}`,
|
||||
details: 'Press on <code>enter/return</code> key after running the command.<br/>'
|
||||
+ 'It will make the file executable.'
|
||||
,
|
||||
},
|
||||
}))
|
||||
.withStep((data) => ({
|
||||
action: {
|
||||
instruction: 'Execute the file:',
|
||||
},
|
||||
code: {
|
||||
instruction: `./${data.fileName}`,
|
||||
details: 'Alternatively you can locate the file in <strong>Finder</strong> and double click on it.',
|
||||
},
|
||||
}))
|
||||
.withStep(() => ({
|
||||
action: {
|
||||
instruction: 'If asked, enter your administrator password.',
|
||||
details: 'As you type, your password will be hidden but the keys are still registered, so keep typing.'
|
||||
+ '<br/>Press on <code>enter/return</code> key after typing your password.'
|
||||
+ '<br/>Administrator privileges are required to configure OS.'
|
||||
,
|
||||
},
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
<template>
|
||||
<div class="instructions">
|
||||
<p>
|
||||
You have two alternatives to apply your selection.
|
||||
</p>
|
||||
<hr />
|
||||
<p>
|
||||
<strong>1. The easy alternative</strong>. Run your script without any manual steps by
|
||||
<a :href="this.macOsDownloadUrl">downloading desktop version</a> of {{ this.appName }} on the
|
||||
{{ this.osName }} system you wish to configure, and then click on the Run button. This is
|
||||
recommended for most users.
|
||||
</p>
|
||||
<hr />
|
||||
<p>
|
||||
<strong>2. The hard (manual) alternative</strong>. This requires you to do additional manual
|
||||
steps. If you are unsure how to follow the instructions, hover on information
|
||||
(<font-awesome-icon :icon="['fas', 'info-circle']" />)
|
||||
icons near the steps, or follow the easy alternative described above.
|
||||
</p>
|
||||
<p>
|
||||
<ol>
|
||||
<li
|
||||
v-for='(step, index) in this.data.steps'
|
||||
v-bind:key="index"
|
||||
class="step"
|
||||
>
|
||||
<div class="step__action">
|
||||
<span>{{ step.action.instruction }}</span>
|
||||
<font-awesome-icon
|
||||
v-if="step.action.details"
|
||||
class="explanation"
|
||||
:icon="['fas', 'info-circle']"
|
||||
v-tooltip.top-center="step.action.details"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="step.code" class="step__code">
|
||||
<Code>{{ step.code.instruction }}</Code>
|
||||
<font-awesome-icon
|
||||
v-if="step.code.details"
|
||||
class="explanation"
|
||||
:icon="['fas', 'info-circle']"
|
||||
v-tooltip.top-center="step.code.details"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
import { ApplicationFactory } from '@/application/ApplicationFactory';
|
||||
import Code from './Code.vue';
|
||||
import { IInstructionListData } from './InstructionListData';
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
Code,
|
||||
},
|
||||
})
|
||||
export default class InstructionList extends Vue {
|
||||
public appName = '';
|
||||
|
||||
public macOsDownloadUrl = '';
|
||||
|
||||
public osName = '';
|
||||
|
||||
@Prop() public data: IInstructionListData;
|
||||
|
||||
public async created() {
|
||||
if (!this.data) {
|
||||
throw new Error('missing data');
|
||||
}
|
||||
const app = await ApplicationFactory.Current.getApp();
|
||||
this.appName = app.info.name;
|
||||
this.macOsDownloadUrl = app.info.getDownloadUrl(OperatingSystem.macOS);
|
||||
this.osName = renderOsName(this.data.operatingSystem);
|
||||
}
|
||||
}
|
||||
|
||||
function renderOsName(os: OperatingSystem): string {
|
||||
switch (os) {
|
||||
case OperatingSystem.Windows: return 'Windows';
|
||||
case OperatingSystem.macOS: return 'macOS';
|
||||
case OperatingSystem.Linux: return 'Linux';
|
||||
default: throw new RangeError(`Cannot render os name: ${OperatingSystem[os]}`);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "@/presentation/assets/styles/main" as *;
|
||||
|
||||
.step {
|
||||
margin: 10px 0;
|
||||
&__action {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
&__code {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
}
|
||||
.explanation {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,16 @@
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
|
||||
export interface IInstructionListData {
|
||||
readonly operatingSystem: OperatingSystem;
|
||||
readonly steps: readonly IInstructionListStep[];
|
||||
}
|
||||
|
||||
export interface IInstructionListStep {
|
||||
readonly action: IInstructionInfo;
|
||||
readonly code?: IInstructionInfo;
|
||||
}
|
||||
|
||||
export interface IInstructionInfo {
|
||||
readonly instruction: string;
|
||||
readonly details?: string;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
import { InstructionsBuilder } from './Data/InstructionsBuilder';
|
||||
import { MacOsInstructionsBuilder } from './Data/MacOsInstructionsBuilder';
|
||||
import { IInstructionListData } from './InstructionListData';
|
||||
|
||||
const builders = new Map<OperatingSystem, InstructionsBuilder>([
|
||||
[OperatingSystem.macOS, new MacOsInstructionsBuilder()],
|
||||
]);
|
||||
|
||||
export function hasInstructions(os: OperatingSystem) {
|
||||
return builders.has(os);
|
||||
}
|
||||
|
||||
export function getInstructions(
|
||||
os: OperatingSystem,
|
||||
fileName: string,
|
||||
): IInstructionListData {
|
||||
return builders
|
||||
.get(os)
|
||||
.build({ fileName });
|
||||
}
|
||||
Reference in New Issue
Block a user