Skip to content

Commit ea3b25c

Browse files
committed
feat(docs): add API Reference links to all platform sections
1 parent aeb9921 commit ea3b25c

File tree

6 files changed

+365
-287
lines changed

6 files changed

+365
-287
lines changed

content/docs/iOS/getting-started.md

Lines changed: 32 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -111,52 +111,50 @@ The `self` in this example means that `MyViewController` also implements `Spokes
111111
Now that we have an instance of `Spokestack`, we'll use the [delegate pattern](https://en.wikipedia.org/wiki/Delegation_pattern) so that the ASR, NLU, and TTS features can send events to you. We'll do that in the same class we used in the previous step. All `SpokestackDelegate` functions are optional except for `failure(error:)`, so you will opt in to each one explictly, but for now we just need to use two of them. First, `failure(error:)`:
112112

113113
```swift
114-
115-
func failure(error: Error) {
116-
print("failure \(String(describing: error))")
117-
}
114+
func failure(error: Error) {
115+
print("failure \(String(describing: error))")
116+
}
118117

119118
```
120119

121120
This will allow Spokestack modules to communicate back to us if something goes wrong. You may also want to listen for `onInit`, which will tell you when Spokestack is ready to start:
122121

123122
```swift
124-
func onInit() {
125-
// ready! time to start listening
126-
spokestack.pipeline.start()
127-
}
123+
func onInit() {
124+
// ready! time to start listening
125+
spokestack.pipeline.start()
126+
}
128127
```
129128

130129
Next, you may not have noticed, but when we built `Spokestack`, we took advantage of a neat feature where Spokestack will automatically classify what the ASR hears. Classifying what your user says into an action in your app is the job of an NLU, or natural language understanding, module. Earlier we configured Spokestack to classify what your user says in terms of guessing a number. Let's wire that up so that we can see what it comes up with!
131130

132131
```swift
133-
func classification(result: NLUResult) {
134-
let intent = result.intent
135-
let slots = result.slots
136-
switch result.intent {
137-
// your app picks a number. the user guess a number.
138-
case "NumberGuessIntent":
139-
let guess = result.slots!["number"]!.value as! Int
140-
// if the guess is higher, lower, or equal to the pick, respond accordingly
141-
return
142-
case "AMAZON.HelpIntent":
143-
// if the user asks for help, respond with the rules of the game
144-
return
145-
case "AMAZON.StopIntent":
146-
// If the user wants to stop playing, then pick another number
147-
return
148-
case default:
149-
// what your user said doesn't make sense in terms of a high/low guessing game, so give them a nudge along the right direction
150-
return
151-
}
132+
func classification(result: NLUResult) {
133+
let intent = result.intent
134+
let slots = result.slots
135+
switch result.intent {
136+
// your app picks a number. the user guess a number.
137+
case "NumberGuessIntent":
138+
let guess = result.slots!["number"]!.value as! Int
139+
// if the guess is higher, lower, or equal to the pick, respond accordingly
140+
return
141+
case "AMAZON.HelpIntent":
142+
// if the user asks for help, respond with the rules of the game
143+
return
144+
case "AMAZON.StopIntent":
145+
// If the user wants to stop playing, then pick another number
146+
return
147+
case default:
148+
// what your user said doesn't make sense in terms of a high/low guessing game, so give them a nudge along the right direction
149+
return
152150
}
151+
}
153152

154-
// ...other delegate functions...
155-
156-
func didTrace(_ trace: String) {
157-
// Get Spokestack module tracing messages that provide additional debugging information. Note that tracing verbosity of each is determined by the SpeechConfiguration.tracing setting!
158-
}
153+
// ...other delegate functions...
159154

155+
func didTrace(_ trace: String) {
156+
// Get Spokestack module tracing messages that provide additional debugging information. Note that tracing verbosity of each is determined by the SpeechConfiguration.tracing setting!
157+
}
160158
```
161159

162160
You'll note that the intents are just a single string. A intent-based classifier will regularize all sorts of related langague into a single canonical intent, eg "let's go" or "please cease" get classified as `start` and `stop`, respectively.
@@ -170,7 +168,7 @@ If you want full hands-free and eyes-free interaction, you'll want to deliver re
170168
```swift
171169
import AVFoundation
172170

173-
...
171+
// ...
174172

175173
class MyViewController: UIViewController, SpokestackDelegate {
176174

@@ -195,7 +193,7 @@ To play the voice response represented by your input with the default audio syst
195193
// uses default SpeechConfiguration values for api access.
196194
let tts = TextToSpeech(self, configuration: SpeechConfiguration())
197195

198-
...
196+
// ...
199197

200198
func speak(_ text: String) {
201199
let input = TextToSpeechInput(text)

content/docs/nav.json

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@
1717
"TTS (iOS)",
1818
"Cookbook (iOS)",
1919
"Tray (iOS)",
20+
{
21+
"external": true,
22+
"href": "https://spokestack.github.io/spokestack-ios/",
23+
"navId": "API Reference (iOS)",
24+
"section": "iOS",
25+
"title": "API Reference"
26+
},
2027
"Overview (Android)",
2128
"Getting Started (Android)",
2229
"Spokestack Configuration (Android)",
@@ -25,6 +32,13 @@
2532
"TTS (Android)",
2633
"Cookbook (Android)",
2734
"Tray (Android)",
35+
{
36+
"external": true,
37+
"href": "https://www.javadoc.io/doc/io.spokestack/spokestack-android/latest/index.html",
38+
"navId": "API Reference (Android)",
39+
"section": "Android",
40+
"title": "API Reference"
41+
},
2842
"Overview (react-native)",
2943
"Getting Started (react-native)",
3044
"Speech Pipeline (react-native)",
@@ -33,20 +47,40 @@
3347
"TTS (react-native)",
3448
"Cookbook (react-native)",
3549
"Tray (react-native)",
50+
{
51+
"external": true,
52+
"href": "https://github.com/spokestack/react-native-spokestack#readme",
53+
"navId": "API Reference (react-native)",
54+
"section": "React Native",
55+
"title": "API Reference"
56+
},
3657
"Overview (Python)",
3758
"Getting Started (Python)",
3859
"Speech Pipeline (Python)",
3960
"NLU (Python)",
4061
"TTS (Python)",
62+
{
63+
"external": true,
64+
"href": "https://spokestack.readthedocs.io/en/stable/",
65+
"navId": "API Reference (Python)",
66+
"section": "Python",
67+
"title": "API Reference"
68+
},
4169
"Overview (Node)",
4270
"Getting Started (Node)",
71+
{
72+
"external": true,
73+
"href": "https://github.com/spokestack/node-spokestack#readme",
74+
"navId": "API Reference (Node)",
75+
"section": "Node",
76+
"title": "API Reference"
77+
},
4378
"Getting Started (Design)",
4479
"Find the Right Use Case",
4580
"Map Out Integration",
4681
"Script Responses",
4782
"Tips for Designing Visual Output",
4883
"Tips for Writing Dialogue",
49-
"Further Learning",
5084
"Export (Integrations)",
5185
"Speech Pipeline Configuration (Machine Learning)",
5286
"Wake Word Models (Machine Learning)",

src/components/DocsPage.tsx

Lines changed: 19 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,23 @@
11
import * as theme from '../styles/theme'
22

3-
import { PageRendererProps, graphql, useStaticQuery } from 'gatsby'
3+
import { Global, css } from '@emotion/react'
44
import { Mdx, Query } from '../utils/graphql'
5-
import { MDXRenderer } from 'gatsby-plugin-mdx'
5+
import { PageRendererProps, graphql, useStaticQuery } from 'gatsby'
6+
import orderDocsLinks, { DocsLinks } from '../utils/orderDocsLinks'
7+
68
import Create from './Create'
79
import DarkModeButton from '../components/DarkModeButton'
810
import Layout from '../components/Layout'
9-
import React from 'react'
11+
import { MDXRenderer } from 'gatsby-plugin-mdx'
1012
import { PageContext } from '../types'
13+
import React from 'react'
14+
import Related from './Related'
1115
import SEO from '../components/SEO'
12-
import { StickyLink } from '../components/StickyNav'
1316
import StickyNavLayout from '../components/StickyNavLayout'
14-
import { css, Global } from '@emotion/react'
15-
import difference from 'lodash/difference'
1617
import findImage from '../utils/findImage'
18+
import hashToId from '../utils/hashToId'
1719
import { isLoggedIn } from '../utils/auth'
18-
import order from '../../content/docs/nav.json'
1920
import removeTrailingSlash from '../utils/removeTrailingSlash'
20-
import sortBy from 'lodash/sortBy'
21-
import uniqBy from 'lodash/uniqBy'
22-
import Related from './Related'
23-
import hashToId from '../utils/hashToId'
2421

2522
interface Props {
2623
location: PageRendererProps['location']
@@ -29,53 +26,34 @@ interface Props {
2926
selectFirst?: boolean
3027
}
3128

32-
function checkDups(links: StickyLink[]) {
33-
const unique = uniqBy(links, (link) => link.navId)
34-
if (unique.length !== links.length) {
35-
const diff = difference(links, unique)
36-
throw new Error(
37-
`The following navIds are not unique: ${diff
38-
.map((link) => link.navId)
39-
.join(', ')}.`
40-
)
41-
}
42-
}
43-
44-
function orderLinks(links: StickyLink[]) {
45-
checkDups(links)
46-
return sortBy(links, (link) => {
47-
const index = order.indexOf(link.navId)
48-
if (index === -1) {
49-
throw new Error(
50-
`Docs page with title ${link.title} has navId ${link.navId}, which does not exist in nav.json.`
51-
)
52-
}
53-
return index
54-
})
55-
}
56-
5729
export default function DocsPage({
5830
location,
5931
post,
6032
related,
6133
selectFirst
6234
}: Props) {
63-
const links: StickyLink[] = []
35+
const links: DocsLinks = {}
6436
const data = useStaticQuery<Query>(docsPageQuery)
6537
const posts = data.allMdx.edges
6638
posts.forEach(({ node }) => {
6739
const fields = node.fields!
6840
const frontmatter = node.frontmatter!
6941
const title = frontmatter.title!
70-
links.push({
42+
const navId = frontmatter.navId!
43+
if (links[navId] != null) {
44+
throw new Error(
45+
`The navId "${navId}" is not unique. Ensure only one doc has this navId.`
46+
)
47+
}
48+
links[navId] = {
7149
href: fields.slug!,
7250
section: fields.folder!,
7351
title,
74-
navId: frontmatter.navId!,
52+
navId,
7553
navTitle: frontmatter.navTitle || title
76-
})
54+
}
7755
})
78-
const orderedLinks = orderLinks(links)
56+
const orderedLinks = orderDocsLinks(links)
7957
if (selectFirst) {
8058
orderedLinks[0].forceSelect = true
8159
}

src/components/StickyNavSection.tsx

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@ import * as theme from '../styles/theme'
33
import { Global, css } from '@emotion/react'
44
import React, { MutableRefObject, useState } from 'react'
55

6+
import Color from 'color'
67
import SVGIcon from './SVGIcon'
78
import type { WindowLocation } from '@reach/router'
89
import currentPath from '../utils/currentPath'
910
import getHash from '../utils/getHash'
1011
import hashToId from '../utils/hashToId'
11-
import Color from 'color'
1212

1313
export interface StickyLink {
14+
// Specifies an external link
15+
external?: boolean
1416
forceSelect?: boolean
1517
href: string
1618
matchHash?: boolean
@@ -65,8 +67,12 @@ export default function StickyNavSection({
6567
6668
html.dark-mode {
6769
.sticky-nav-link,
68-
.sticky-nav-link:visited {
70+
.sticky-nav-link:visited:not(:hover) {
6971
color: ${Color('#ffffff').fade(0.2).toString()};
72+
73+
.icon {
74+
fill: ${Color('#ffffff').fade(0.2).toString()};
75+
}
7076
}
7177
.nav-selected-bg,
7278
.sticky-nav-link.active {
@@ -130,6 +136,13 @@ export default function StickyNavSection({
130136
href={link.href}
131137
title={link.title}>
132138
{link.navTitle || link.title}
139+
{link.external && (
140+
<SVGIcon
141+
icon="#external"
142+
className="icon"
143+
extraCss={styles.externalIcon}
144+
/>
145+
)}
133146
</a>
134147
)
135148
})}
@@ -182,23 +195,40 @@ const styles = {
182195
}
183196
`,
184197
stickyNavLink: css`
198+
display: flex;
199+
flex-direction: row;
200+
justify-content: flex-start;
201+
align-items: center;
185202
line-height: 1.2;
186203
padding: 12px 20px 12px 22px;
187204
text-decoration: none;
188205
user-select: none;
189206
font-size: 14px;
190207
191208
&,
192-
&:visited {
209+
&:visited:not(:hover) {
193210
color: ${theme.headerColor.fade(0.25).toString()};
211+
212+
.icon {
213+
fill: ${theme.headerColor.fade(0.25).toString()};
214+
}
194215
}
195216
&:hover {
196217
color: ${theme.linkHover};
218+
219+
.icon {
220+
fill: ${theme.linkHover};
221+
}
197222
}
198223
&.active,
199224
&.active-no-bg {
200225
cursor: default;
201226
pointer-events: none;
202227
}
228+
`,
229+
externalIcon: css`
230+
width: 10px;
231+
height: 10px;
232+
margin-left: 7px;
203233
`
204234
}

0 commit comments

Comments
 (0)