Skip to content

Commit b58a749

Browse files
committed
improvement(links): share the go/ and sim/ host list and show link names without a prefix
1 parent ce32a32 commit b58a749

25 files changed

Lines changed: 97 additions & 72 deletions

File tree

‎apps/docs/content/docs/search/sim-links.mdx‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: Links
33
description: Short go/ and sim/ links that take everyone in your organization to the right page
44
---
55

6-
Links are short names for pages your team opens often, like `sim/handbook` or `sim/okta`. With Sim for Chrome, `go/handbook` opens the same link, so the go/ habit from other companies works too. Every member of your organization can use them, with or without a Search seat.
6+
Links are short names for pages your team opens often, like `handbook` or `okta`. They open as `sim/handbook` or `go/handbook`. Every member of your organization can use them, with or without a Search seat.
77

88
Open **Links** in the organization sidebar to see your organization's links. Search by name, destination, or description, or use **Sort → Opens** to see the 50 most opened links.
99

@@ -41,11 +41,11 @@ Organization admins can also open **Browser shortcuts → Team bookmarks**, choo
4141

4242
## Create a link
4343

44-
1. Open **Sim Links** and select **New link**.
44+
1. Open **Links** and select **New link**.
4545
2. Enter the **Destination URL** and a **Name**. Names use letters, numbers, and `. : @ & ~ - _`; spaces become hyphens as you type.
4646
3. Optionally add a **Description**, then select **Create**.
4747

48-
With Sim for Chrome 0.1.3 or later, right-click an HTTP or HTTPS link on a page and choose **Create Sim Link**. Check the destination and organization, enter a name, then choose **Create**. Sim strips query parameters and fragments from the initial destination. You can edit the address before saving. Closing the draft creates nothing.
48+
With Sim for Chrome 0.1.3 or later, right-click an HTTP or HTTPS link on a page and choose **Create link in Sim**. Check the destination and organization, enter a name, then choose **Create**. Sim strips query parameters and fragments from the initial destination. You can edit the address before saving. Closing the draft creates nothing.
4949

5050
To share a link, right-click it and select **Copy link**.
5151

@@ -67,7 +67,7 @@ Values fill each `{*}` in order, and the last `{*}` takes the rest. A link with
6767

6868
By default, any member can edit or delete a link. The link's creator and organization admins can change two options under **Access**:
6969

70-
- **Hide from the Sim Links page**: only the creator and admins see the link in the list, marked **Unlisted**. Anyone with the name can still open it.
70+
- **Hide from the Links page**: only the creator and admins see the link in the list, marked **Unlisted**. Anyone with the name can still open it.
7171
- **Only the creator and admins can edit**: only the creator and admins can edit or delete the link, marked **Restricted**.
7272

7373
Changes to links are recorded in your organization's [audit logs](/platform/enterprise/audit-logs).

‎apps/extension/README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ SIM_EXTENSION_DEV=1 SIM_EXTENSION_ORIGIN=http://localhost:3000 bun run --cwd app
3030
## Data and permissions
3131

3232
- Required capabilities: `activeTab`, `scripting`, `sidePanel`, `storage`, `contextMenus`, `declarativeNetRequestWithHostAccess`, and access to the configured Sim origin and to `http(s)://sim/*` and `http(s)://go/*`.
33-
- Type `sim`, press Space, enter the name, and press Enter to open an organization's [Sim link](https://docs.sim.ai/search/sim-links). `go/<name>` opens the same link, for people used to go links elsewhere. Chrome can treat bare `go/<name>` or `sim/<name>` as a search before the redirect rule runs. Typing `http://go/<name>` once lets Chrome learn the short host in that profile. A trailing slash (`go/<name>/`) also selects navigation without relying on that history. A single static redirect rule rewrites top-level navigations to `http(s)://sim/*` and `http(s)://go/*` to `<Sim origin>/sim/*` with the path and query unchanged. The `sim` and `go` host permissions exist only for that rule: every navigation to those hosts is redirected before it loads, and no other request is matched or observed. An organization that already serves an intranet host named `sim` or `go` should not install this build, since its links would be redirected to Sim. When another extension (such as another go-links tool) also redirects `go/`, Chrome applies the most recently installed extension's rule.
33+
- Type `sim`, press Space, enter the name, and press Enter to open an organization's [link](https://docs.sim.ai/search/sim-links). `go/<name>` opens the same link. Chrome can treat bare `go/<name>` or `sim/<name>` as a search before the redirect rule runs. Typing `http://go/<name>` once lets Chrome learn the short host in that profile. A trailing slash (`go/<name>/`) also selects navigation without relying on that history. A single static redirect rule rewrites top-level navigations to `http(s)://sim/*` and `http(s)://go/*` to `<Sim origin>/sim/*` with the path and query unchanged. The `sim` and `go` host permissions exist only for that rule: every navigation to those hosts is redirected before it loads, and no other request is matched or observed. An organization that already serves an intranet host named `sim` or `go` should not install this build, since its links would be redirected to Sim. When another extension (such as another go-links tool) also redirects `go/`, Chrome applies the most recently installed extension's rule.
3434
- The `sim` address-bar keyword (`sim`, then Space) lists matching Sim links while you type and opens what you enter through the same resolver. For these suggestions, the background worker sends the text typed before the first `/` or `?`, and nothing else, unless that text contains a space (a search) or is longer than a link name can be, to `<Sim origin>/api/sim-links/suggestions` with the signed-in session, and the extension's content security policy allows connections to that origin alone. Unlisted links are never suggested.
3535
- **+ → Browser shortcuts** separately opts into recovery for exact Google, Bing, DuckDuckGo, or Yahoo HTTPS search hosts. After a direct Chrome permission grant, dynamic request rules redirect only top-level GET requests with one query parameter containing a whole `go/<name>` or `sim/<name>` shortcut. Only that value reaches `/sim-links/recover`, which validates it and redirects to the existing authenticated resolver. Ordinary search terms, additional search parameters, subframes, and unsupported inputs are not forwarded. Chrome applies these rules without an extension search-URL observer. Turning a provider off removes its rules while preserving site access; revoking access clears its recovery choice. This does not enable page reference or change the default search provider.
3636
- Optional HTTP/HTTPS site access is requested only by a menu action naming that site. Chrome's `activeTab` access is temporary and does not follow arbitrary new sites.

‎apps/extension/scripts/build.ts‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { createPublicKey } from 'node:crypto'
22
import { cp, mkdir, readFile, rm, writeFile } from 'node:fs/promises'
33
import { dirname, resolve } from 'node:path'
44
import { fileURLToPath } from 'node:url'
5+
import { SIM_LINK_HOST_PATTERN, SIM_LINK_HOSTS } from '@sim/browser-extension-protocol'
56
import { build } from 'esbuild'
67
import postcss from 'postcss'
78
import loadPostcssConfig from 'postcss-load-config'
@@ -90,16 +91,18 @@ for (const [size, file] of [
9091
await cp(resolve(root, '../sim/public/favicon', file), resolve(dist, `icon-${size}.png`))
9192
}
9293
/**
93-
* Only requests to the short hosts reach this rule: `go/` is the habit people bring from other
94-
* companies, `sim/` our own. Chrome can classify a bare `go/<name>` as a search before navigation.
95-
* Preserve the path and query for actual short-host navigations.
94+
* Only requests to the short hosts reach this rule. Chrome can classify a bare `go/<name>` as a
95+
* search before navigation. Preserve the path and query for actual short-host navigations.
9696
*/
9797
const simLinkRules = [
9898
{
9999
id: 1,
100100
priority: 1,
101101
action: { type: 'redirect', redirect: { regexSubstitution: `${origin.origin}/sim/\\1` } },
102-
condition: { regexFilter: '^https?://(?:sim|go)/(.*)$', resourceTypes: ['main_frame'] },
102+
condition: {
103+
regexFilter: `^https?://(?:${SIM_LINK_HOST_PATTERN})/(.*)$`,
104+
resourceTypes: ['main_frame'],
105+
},
103106
},
104107
]
105108
await writeFile(resolve(dist, 'sim-links.json'), JSON.stringify(simLinkRules, null, 2))
@@ -123,10 +126,7 @@ await writeFile(
123126
],
124127
host_permissions: [
125128
`${origin.origin}/*`,
126-
'http://sim/*',
127-
'https://sim/*',
128-
'http://go/*',
129-
'https://go/*',
129+
...SIM_LINK_HOSTS.flatMap((host) => [`http://${host}/*`, `https://${host}/*`]),
130130
],
131131
declarative_net_request: {
132132
rule_resources: [{ id: 'sim_links', enabled: true, path: 'sim-links.json' }],

‎apps/extension/scripts/test-e2e.ts‎

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
type AppToExtensionMessage,
1212
extensionToAppMessageSchema,
1313
pageSnapshotSchema,
14+
SIM_LINK_HOSTS,
1415
} from '@sim/browser-extension-protocol'
1516
import { SecretReveal } from '@sim/emcn'
1617
import { createLogger } from '@sim/logger'
@@ -363,20 +364,17 @@ try {
363364
'HTTP and HTTPS navigations to the sim and go hosts reach the configured resolver',
364365
async () => {
365366
const tab = await page.context().newPage()
366-
for (const [scheme, host] of [
367-
['http', 'sim'],
368-
['https', 'sim'],
369-
['http', 'go'],
370-
['https', 'go'],
371-
]) {
372-
for (const path of [
373-
'handbook',
374-
'handbook/',
375-
'jira/123?view=board',
376-
'jira/a%2Fb?view=board%20view',
377-
]) {
378-
await tab.goto(`${scheme}://${host}/${path}`)
379-
assert(tab.url() === `${origin}/sim/${path}`, `${host}/${path} reached ${tab.url()}`)
367+
for (const scheme of ['http', 'https']) {
368+
for (const host of SIM_LINK_HOSTS) {
369+
for (const path of [
370+
'handbook',
371+
'handbook/',
372+
'jira/123?view=board',
373+
'jira/a%2Fb?view=board%20view',
374+
]) {
375+
await tab.goto(`${scheme}://${host}/${path}`)
376+
assert(tab.url() === `${origin}/sim/${path}`, `${host}/${path} reached ${tab.url()}`)
377+
}
380378
}
381379
}
382380
await tab.close()
@@ -1439,12 +1437,10 @@ try {
14391437
await unchanged(url)
14401438
await toggle(provider.id, true)
14411439
for (const host of provider.hosts) {
1442-
for (const shortcut of [
1443-
'sim%2Faskit',
1444-
'sim/jira/ABC-123',
1445-
'go%2Faskit',
1446-
'go/jira/ABC-123',
1447-
]) {
1440+
for (const shortcut of SIM_LINK_HOSTS.flatMap((short) => [
1441+
`${short}%2Faskit`,
1442+
`${short}/jira/ABC-123`,
1443+
])) {
14481444
const candidate = `https://${host}${provider.path}?client=chrome&${provider.parameter}=${shortcut}&source=qa`
14491445
const prior = searchRequests.filter((request) => request.url === candidate).length
14501446
await tab.goto(candidate)

‎apps/extension/src/omnibox.ts‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,15 @@ interface SuggestedLink {
2424

2525
/**
2626
* What was typed after the `sim` keyword, split where the name ends: `sim/jira/ABC-1?tab=all`
27-
* (a `sim/` or `go/` prefix is optional) has the name `jira` and the rest `/ABC-1?tab=all`. Surrounding spaces,
27+
* (the `sim/` is optional) has the name `jira` and the rest `/ABC-1?tab=all`. Surrounding spaces,
2828
* the prefix, leading slashes, and any `#fragment` are dropped, as the address bar would; values
2929
* and the query otherwise stay exactly as typed.
3030
*/
3131
function readTyped(text: string): { name: string; rest: string } {
3232
const typed = text
3333
.trim()
34-
.replace(/^(?:sim|go)\//i, '')
34+
// Only the keyword's own `sim/`: after it, `go/x` can be a link named `go` with a value.
35+
.replace(/^sim\//i, '')
3536
.replace(/^\/+/, '')
3637
.replace(/#.*$/, '')
3738
const nameEnd = typed.search(/[/?]/)

‎apps/extension/src/search-recovery-dialog.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ export function SearchRecoveryDialog({ onClose }: SearchRecoveryDialogProps) {
122122
title={provider.label}
123123
titleActions={
124124
<Switch
125-
aria-label={`${provider.label} sim/ and go/ shortcuts`}
125+
aria-label={`${provider.label} go/ and sim/ shortcuts`}
126126
aria-busy={busyProvider === provider.id}
127127
checked={settings?.[provider.id] ?? false}
128128
disabled={!settings || loadError || busyProvider !== null}

‎apps/extension/src/search-recovery.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { SIM_LINK_HOST_PATTERN } from '@sim/browser-extension-protocol'
12
import { createLogger } from '@sim/logger'
23
import { isRecordLike } from '@sim/utils/object'
34
import { escapeRegExp } from '@sim/utils/string'
@@ -50,7 +51,7 @@ function recoveryRules(
5051
): chrome.declarativeNetRequest.Rule[] {
5152
const hosts = provider.origins.map((origin) => escapeRegExp(new URL(origin).host)).join('|')
5253
const character = '(?:[a-z0-9._~:@/-]|%(?:2[6def]|3[0-9a]|4[0-9a-f]|5[0-9af]|6[1-9a-f]|7[0-9ae]))'
53-
const shortcut = `(?:sim|go)(?:/|%2f)${character}+`
54+
const shortcut = `(?:${SIM_LINK_HOST_PATTERN})(?:/|%2f)${character}+`
5455
const condition = {
5556
isUrlFilterCaseSensitive: false,
5657
requestDomains: provider.origins.map((origin) => new URL(origin).hostname),

‎apps/sim/app/(landing)/privacy/privacy-content.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ function richText(content: string): ReactNode {
4444
export const PRIVACY_CONFIG: LegalPageConfig = {
4545
title: 'Privacy Policy',
4646
description: 'Sim Studio, Inc. · Operating the Sim platform (sim.ai)',
47-
lastUpdated: 'November 3, 2026',
47+
lastUpdated: 'November 4, 2026',
4848
intro: [
4949
{
5050
kind: 'paragraph',
@@ -836,7 +836,7 @@ export const PRIVACY_CONFIG: LegalPageConfig = {
836836
{
837837
kind: 'paragraph',
838838
content: richText(
839-
'When You use the sim address-bar keyword, the extension sends the typed link name, before any path values or query, to Sim with Your authenticated session to suggest Your organization’s links. Suggestions are cached in memory and are not written to extension storage. Opening a sim/ shortcut sends its path and query to Sim to resolve the destination, then opens that destination. Sim records each link’s visit count and last-visited time. These requests also use the operational diagnostics described in this Privacy Policy. Choosing Create Sim Link from the browser menu opens a draft with the selected link’s address, omitting query parameters and fragments. The destination becomes an organization Sim Link only when You confirm creation. The extension does not read Your browser history or unrelated address-bar input.'
839+
'When You use the sim address-bar keyword, the extension sends the typed link name, before any path values or query, to Sim with Your authenticated session to suggest Your organization’s links. Suggestions are cached in memory and are not written to extension storage. Opening a sim/ or go/ shortcut sends its path and query to Sim to resolve the destination, then opens that destination. Sim records each link’s visit count and last-visited time. These requests also use the operational diagnostics described in this Privacy Policy. Choosing Create link in Sim from the browser menu opens a draft with the selected link’s address, omitting query parameters and fragments. The destination becomes an organization link only when You confirm creation. The extension does not read Your browser history or unrelated address-bar input.'
840840
),
841841
},
842842
{

‎apps/sim/app/extension/sign-in.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ function LinkDraftSignIn() {
1313
const href = `/login?callbackUrl=${encodeURIComponent(SIM_LINK_DRAFT_PATH)}`
1414
return (
1515
<main className='flex h-dvh flex-col items-center justify-center gap-4 bg-[var(--bg)] p-6 text-center text-[var(--text-body)] text-small'>
16-
<h1 className='text-lg'>Sign in to create a Sim Link</h1>
16+
<h1 className='text-lg'>Sign in to create a link</h1>
1717
{needsSeparateTab ? (
1818
<>
1919
<p>Sign in in another tab, then reload this draft.</p>

‎apps/sim/app/o/[organizationId]/links/managed-bookmarks.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export function ManagedBookmarks({ organizationId }: ManagedBookmarksProps) {
2727
const visible = new Set(listed.map((link) => link.id))
2828
const options = [...candidates.values()].map((link) => ({
2929
value: link.id,
30-
label: `sim/${link.name}`,
30+
label: link.name,
3131
hidden: !visible.has(link.id),
3232
disabled: selected.length >= MAX_MANAGED_BOOKMARKS && !selectedIds.has(link.id),
3333
}))
@@ -57,7 +57,7 @@ export function ManagedBookmarks({ organizationId }: ManagedBookmarksProps) {
5757
url: new URL(routes.openSimLink(name), window.location.origin).href,
5858
}))
5959
saveBlob(
60-
new Blob([JSON.stringify([{ name: 'Sim Links', children }], null, 2)], {
60+
new Blob([JSON.stringify([{ name: 'Links', children }], null, 2)], {
6161
type: 'application/json',
6262
}),
6363
'sim-managed-bookmarks.json'

0 commit comments

Comments
 (0)