diff --git a/DESIGN.md b/DESIGN.md index fa13d3576..0f34d4e6b 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -339,7 +339,7 @@ A deleted commit that still has visible replies keeps its place so the conversat A quote is a regular commit row with the quoted commit embedded under its body: a hairline-bordered, 8px-rounded block (like a link preview, no lane color) with a 20px avatar, name (600), mono handle, and mono relative time on one line, then the body at 15px, pre-wrapped and clamped to six lines. The whole block is one link to the quoted commit's page and tints Accent on hover/focus; it embeds one level only. A deleted quoted commit is a dashed block with a mono `[deleted]` token and "This commit was deleted."; one the viewer cannot see reads `[unavailable]` and "This commit isn't available.", with no author, time, or body. "Quote" sits in the action row after the star (icon only on phones) and opens a dialog titled "Quote @handle": the viewer's avatar, a 17px textarea ("Add your take") with its own stash, the quoted block as a static preview, a counter, Cancel, and Quote (⌘/Ctrl+Enter). It is hidden wherever the star is. ### Commit Page -Every commit has a page at `/@username/commits/{id}`, reached from its relative time or short hash (both links), the "↳ replying to @handle" label, or Enter on a focused commit. A sticky header reads "Commit" with the mono short hash and a copy-link icon on the right. Visible ancestors (up to 20) stack above as regular commit rows on one continuous rail, without delete, with deleted ones as tombstones; the focused commit sits last with a 17px body, a mono "time · date · commit hash" line, and its replies already expanded. A deleted commit's own page is a 404. +Every commit has a page at `/@handle/commits/{id}`, reached from its relative time or short hash (both links), the "↳ replying to @handle" label, or Enter on a focused commit. A sticky header reads "Commit" with the mono short hash and a copy-link icon on the right. Visible ancestors (up to 20) stack above as regular commit rows on one continuous rail, without delete, with deleted ones as tombstones; the focused commit sits last with a 17px body, a mono "time · date · commit hash" line, and its replies already expanded. A deleted commit's own page is a 404. ### People Search A command palette opened by ⌘K / Ctrl+K or `/` anywhere, the "Search developers ⌘K" control at the top of the right column, a Search nav item where that column is hidden, or the search icon in the mobile header. Popover surface, 576px max, 12vh from the top: a borderless 17px input with a search icon and an `esc` cap, results as rows (36px avatar, name in 600, mono handle with lane dot, truncated bio) with the active row on Accent and a `↵` hint, and a footer of key hints (hidden on phones). Combobox semantics: arrow keys move, Enter opens the profile, Escape closes. Empty: "Type a name or @handle."; pending: "Searching…"; no match: mono `fatal: no developers match '…'`. diff --git a/KNOWLEDGE_BASE.md b/KNOWLEDGE_BASE.md index 19e85dff8..d0b97ee76 100644 --- a/KNOWLEDGE_BASE.md +++ b/KNOWLEDGE_BASE.md @@ -98,7 +98,7 @@ Sources: [onboarding page](resources/js/pages/onboarding/show.tsx), [sign-in tes ### Profiles and GitHub sync -Every developer has a profile at `/@{username}`, with their GitHub identity, bio and other public profile information, commit/following/follower counts, a **Follows you** badge when they follow you, and an activity graph. Across a block in either direction, the profile is shown as unavailable. The Commits tab lists top-level commits; Replies lists that developer's replies with parent context; README shows the README of their GitHub profile repository (the one named after their username), as GitHub renders it, refreshed daily. It keeps only plain formatting, links, and images hosted by GitHub: no scripts, styles, or classes. When GitHub doesn't answer, the tab says so rather than showing no README. Follow and follower counts open the corresponding people lists. The profile links to GitHub and, when available, GitHub Sponsors. Share uses the device's share sheet when supported, or copies the profile link. +Every developer has a profile at `/@{handle}`, with their GitHub identity, bio and other public profile information, commit/following/follower counts, a **Follows you** badge when they follow you, and an activity graph. Across a block in either direction, the profile is shown as unavailable. The Commits tab lists top-level commits; Replies lists that developer's replies with parent context; README shows the README of their GitHub profile repository (the one named after their username), as GitHub renders it, refreshed daily. It keeps only plain formatting, links, and images hosted by GitHub: no scripts, styles, or classes. When GitHub doesn't answer, the tab says so rather than showing no README. Follow and follower counts open the corresponding people lists. The profile links to GitHub and, when available, GitHub Sponsors. Share uses the device's share sheet when supported, or copies the profile link. Change your name, avatar, bio, company, location, or website on GitHub, not on /dev/null. Settings → Profile and the **Edit profile** menu on your own profile offer **Edit on GitHub** and **Sync from GitHub**. Profile details also refresh on sign-in. Manual sync is limited to once a minute. The GitHub Sponsors link is refreshed on sign-in only. A GitHub username rename is picked up on sign-in or sync; if a stale account still holds the new username because its owner also renamed, that account moves to a `_renamed-` placeholder until its owner signs in or syncs again. Accounts are matched by GitHub id and never merged. @@ -420,7 +420,7 @@ Sources: [web app manifest](public/site.webmanifest), [service worker](public/sw ### Activity graphs and README embeds -Profiles display commit activity with current and longest streaks. The profile graph adapts to how long someone has been committing: the last 24 hours, 7 days, 30 days, or a year. Hovering a cell highlights that period's commits. Settings → Profile includes a live preview and copyable Markdown for embedding your /dev/null activity graph in a GitHub profile README. The graph is public at `/@{username}/graph.svg`, uses your lane color, and supports `?theme=light`, `dark`, or `auto` (light by default). The SVG is cached for an hour, so a new commit may not appear in an external embed immediately. +Profiles display commit activity with current and longest streaks. The profile graph adapts to how long someone has been committing: the last 24 hours, 7 days, 30 days, or a year. Hovering a cell highlights that period's commits. Settings → Profile includes a live preview and copyable Markdown for embedding your /dev/null activity graph in a GitHub profile README. The graph is public at `/@{handle}/graph.svg`, uses your lane color, and supports `?theme=light`, `dark`, or `auto` (light by default). The SVG is cached for an hour, so a new commit may not appear in an external embed immediately. Sources: [graph embed settings](resources/js/components/activity-graph-embed.tsx), [graph controller](app/Http/Controllers/UserActivityGraphController.php), [graph route and cache](routes/web.php). @@ -462,9 +462,9 @@ Sources: [workflow instructions](resources/js/pages/terminal/show.tsx), [GitHub ### Public read API and Atom feeds -The read-only JSON API at `/api/v1` requires no authentication or token. Read a public profile at `/api/v1/users/{username}`, that user's commits at `/api/v1/users/{username}/commits`, or one commit at `/api/v1/commits/{commit}`. `/api/v1/users/{username}/followers` and `/following` list people the way the profile's people lists do, newest follow first; a signed request also gets each person's lane and whether you follow each other. Add `?replies=1` to the user-commits endpoint to read their replies instead of top-level commits. Lists use cursor pagination: 20 commits or 30 people per page. Only public approved, non-deleted content is returned, not private profile data such as email. Unsigned requests are limited to **60 requests per minute per IP** and cannot create posts, follow, or star; terminal posting and the [signed API](#signed-api-and-devices) are separate signed interfaces. +The read-only JSON API at `/api/v1` requires no authentication or token. Read a public profile at `/api/v1/users/{handle}`, that user's commits at `/api/v1/users/{handle}/commits`, or one commit at `/api/v1/commits/{commit}`. `/api/v1/users/{handle}/followers` and `/following` list people the way the profile's people lists do, newest follow first; a signed request also gets each person's lane and whether you follow each other. Add `?replies=1` to the user-commits endpoint to read their replies instead of top-level commits. Lists use cursor pagination: 20 commits or 30 people per page. Only public approved, non-deleted content is returned, not private profile data such as email. Unsigned requests are limited to **60 requests per minute per IP** and cannot create posts, follow, or star; terminal posting and the [signed API](#signed-api-and-devices) are separate signed interfaces. -Subscribe in a feed reader to `/@{username}/feed.atom` for a profile's latest **50 approved top-level commits**, including image enclosures. Feeds are public and can be cached for five minutes. +Subscribe in a feed reader to `/@{handle}/feed.atom` for a profile's latest **50 approved top-level commits**, including image enclosures. Feeds are public and can be cached for five minutes. Sources: [API routes](routes/api.php), [API controllers](app/Http/Controllers/Api), [feed generation](app/Actions/CreateUserFeed.php), [rate limits](app/Providers/AppServiceProvider.php). diff --git a/PRODUCT.md b/PRODUCT.md index e5c2f1be7..a874d12eb 100644 --- a/PRODUCT.md +++ b/PRODUCT.md @@ -59,7 +59,7 @@ Deliberately excluded: sign-up or identity outside GitHub, editing commits, and - Voice: dry, terminal-literate humor. Understated wit; copy may wink at Unix and `/dev/null`, but stays clear and never obscures what an action does. - Lore is part of the product: Tom (everyone's first friend, after MySpace Tom), pastéis (a tribute to the pastéis de nata brought to the livestream), @world, the Unix-named achievements. Extend it with restraint, never at the expense of clarity. The stories behind it are in the knowledge base. - Current tagline in use: "A social network for developers." -- Vocabulary: a post is a **commit** and a timeline is a **log**, in the UI and in code (`Commit` model, `LogController`, `/log/{log}`). A GitHub account mirrored at its `/@handle` is a **profile** (`Profile` model, as GitHub's `ProfileOwner`), and the unique name GitHub calls its login is its **handle** in code; the API also sends it as `username` for older clients. The git metaphor stops at those two nouns, except inside Remotes, which carries its own small vocabulary: a conversation is a **remote**, sending is a **push**, a read receipt is **pulled**, a request is **fetched** until **merged** or **dropped**, archiving is **stash** and **pop**, a group is an **org** of **maintainers** and **contributors**, and **HEAD** marks where you left off. No branches, forks, diffs, or PRs. **Star** is the engagement verb, not a git ref. +- Vocabulary: a post is a **commit** and a timeline is a **log**, in the UI and in code (`Commit` model, `LogController`, `/log/{log}`). A GitHub account mirrored at its `/@handle` is a **profile** (`Profile` model, as GitHub's `ProfileOwner`), and the unique name GitHub calls its login is its **handle** in code and in the API. The git metaphor stops at those two nouns, except inside Remotes, which carries its own small vocabulary: a conversation is a **remote**, sending is a **push**, a read receipt is **pulled**, a request is **fetched** until **merged** or **dropped**, archiving is **stash** and **pop**, a group is an **org** of **maintainers** and **contributors**, and **HEAD** marks where you left off. No branches, forks, diffs, or PRs. **Star** is the engagement verb, not a git ref. ## Evidence on Hand diff --git a/app/Http/Requests/ListApiSuggestedUsersRequest.php b/app/Http/Requests/ListApiSuggestedUsersRequest.php index 6a2d49a98..90fcb7cb0 100644 --- a/app/Http/Requests/ListApiSuggestedUsersRequest.php +++ b/app/Http/Requests/ListApiSuggestedUsersRequest.php @@ -15,7 +15,7 @@ /** * Who to follow may be asked for the page it shows on, as the web asks for it beside a profile or a commit. The commit - * has to be one the user can see, as its page requires. The API calls a handle a username, as in its user resources. + * has to be one the user can see, as its page requires. */ final class ListApiSuggestedUsersRequest extends FormRequest { @@ -29,8 +29,8 @@ public function rules(): array return [ 'round' => ['nullable', 'integer', 'min:0'], - 'username' => ['nullable', 'string', Rule::exists('profiles', 'handle')->where('type', ProfileType::User->value)], - 'commit_id' => ['bail', 'nullable', 'uuid', 'prohibits:username', new VisibleCommit($user, __('That commit is no longer available.'))], + 'handle' => ['nullable', 'string', Rule::exists('profiles', 'handle')->where('type', ProfileType::User->value)], + 'commit_id' => ['bail', 'nullable', 'uuid', 'prohibits:handle', new VisibleCommit($user, __('That commit is no longer available.'))], ]; } @@ -40,7 +40,7 @@ public function context(): User|Commit|null assert($user instanceof User); return match (true) { - $this->filled('username') => User::query()->whereRelation('profile', 'handle', $this->string('username')->value())->firstOrFail(), + $this->filled('handle') => User::query()->whereRelation('profile', 'handle', $this->string('handle')->value())->firstOrFail(), $this->filled('commit_id') => Commit::query()->visibleTo($user)->findOrFail($this->string('commit_id')->value()), default => null, }; diff --git a/app/Http/Resources/UserResource.php b/app/Http/Resources/UserResource.php index 273089b39..4d30cbf0a 100644 --- a/app/Http/Resources/UserResource.php +++ b/app/Http/Resources/UserResource.php @@ -25,7 +25,6 @@ public function toArray(Request $request): array return [ 'id' => $this->id, 'handle' => $this->handle, - 'username' => $this->handle, 'name' => $this->name, 'avatar_url' => $this->avatar_url, 'bio' => $this->bio, diff --git a/app/Services/Mentions.php b/app/Services/Mentions.php index 7142b70d1..c74a2ffca 100644 --- a/app/Services/Mentions.php +++ b/app/Services/Mentions.php @@ -36,9 +36,9 @@ public function handles(string $text): array /** * Finds where each "@handle" is in the text, by the same rules and also skipping code, so a client can link it * to the profile like the web does. The handle keeps its case, and the offset and length count characters in - * the whole text and include the "@". The handle is also the username, which clients read before handles. + * the whole text and include the "@". * - * @return list + * @return list */ public function positions(string $text): array { @@ -52,7 +52,6 @@ public function positions(string $text): array foreach ($matches as $match) { $positions[] = [ 'handle' => $match[1][0], - 'username' => $match[1][0], 'offset' => $offset + mb_strlen(mb_strcut($part, 0, $match[0][1])), 'length' => mb_strlen($match[0][0]), ]; diff --git a/tests/Feature/Controllers/Api/CommitControllerTest.php b/tests/Feature/Controllers/Api/CommitControllerTest.php index 1eb4c87d5..1d9199f10 100644 --- a/tests/Feature/Controllers/Api/CommitControllerTest.php +++ b/tests/Feature/Controllers/Api/CommitControllerTest.php @@ -46,7 +46,6 @@ 'author' => [ 'id' => $user->id, 'handle' => 'octocat', - 'username' => 'octocat', 'name' => 'The Octocat', 'avatar_url' => 'https://avatars.githubusercontent.com/u/583231', 'bio' => $user->bio, @@ -222,7 +221,6 @@ $author = fn (User $author): array => [ 'id' => $author->id, 'handle' => $author->handle, - 'username' => $author->handle, 'name' => $author->name, 'avatar_url' => $author->avatar_url, 'bio' => $author->bio, @@ -257,7 +255,7 @@ ['type' => 'code', 'code' => '@Override'], ], 'preview_segments' => null, - 'mentions' => [['handle' => 'nunomaduro', 'username' => 'nunomaduro', 'offset' => 4, 'length' => 11]], + 'mentions' => [['handle' => 'nunomaduro', 'offset' => 4, 'length' => 11]], 'status' => 'approved', 'is_pinned' => false, 'starred' => true, diff --git a/tests/Feature/Controllers/Api/ConversationMessageControllerTest.php b/tests/Feature/Controllers/Api/ConversationMessageControllerTest.php index 8a22dc7ff..d46bf308b 100644 --- a/tests/Feature/Controllers/Api/ConversationMessageControllerTest.php +++ b/tests/Feature/Controllers/Api/ConversationMessageControllerTest.php @@ -49,7 +49,7 @@ ['type' => 'code', 'code' => 'the PR'], ['type' => 'text', 'text' => ', @ghost? https://laravel.com'], ]) - ->assertJsonPath('data.messages.0.mentions', [['handle' => 'octocat', 'username' => 'octocat', 'offset' => 4, 'length' => 8]]) + ->assertJsonPath('data.messages.0.mentions', [['handle' => 'octocat', 'offset' => 4, 'length' => 8]]) ->assertJsonPath('data.messages.0.link_preview.url', 'https://laravel.com') ->assertJsonPath('data.messages.0.link_preview.is_pending', true) ->assertJsonPath('data.messages.0.reply_to', null) diff --git a/tests/Feature/Controllers/Api/CurrentUserControllerTest.php b/tests/Feature/Controllers/Api/CurrentUserControllerTest.php index 326fa5f12..592d69bcd 100644 --- a/tests/Feature/Controllers/Api/CurrentUserControllerTest.php +++ b/tests/Feature/Controllers/Api/CurrentUserControllerTest.php @@ -38,7 +38,6 @@ 'data' => [ 'id' => $user->id, 'handle' => 'octocat', - 'username' => 'octocat', 'name' => 'The Octocat', 'avatar_url' => 'https://avatars.githubusercontent.com/u/583231', 'bio' => 'I am the Octocat.', diff --git a/tests/Feature/Controllers/Api/LogControllerTest.php b/tests/Feature/Controllers/Api/LogControllerTest.php index 03fbb95e8..1e64ae1b9 100644 --- a/tests/Feature/Controllers/Api/LogControllerTest.php +++ b/tests/Feature/Controllers/Api/LogControllerTest.php @@ -60,7 +60,6 @@ 'author' => [ 'id' => $author->id, 'handle' => 'octocat', - 'username' => 'octocat', 'name' => 'The Octocat', 'avatar_url' => 'https://avatars.githubusercontent.com/u/583231', 'bio' => $author->bio, @@ -119,7 +118,7 @@ ->assertJsonPath('data.0.stars_count', 2) ->assertJsonPath('data.0.starred', true) ->assertJsonPath('data.0.is_pinned', true) - ->assertJsonPath('data.0.mentions', [['handle' => 'NunoMaduro', 'username' => 'NunoMaduro', 'offset' => 4, 'length' => 11]]) + ->assertJsonPath('data.0.mentions', [['handle' => 'NunoMaduro', 'offset' => 4, 'length' => 11]]) ->assertJsonPath('data.0.link_preview', [ 'url' => 'https://laravel.com', 'title' => $linkPreview->title, diff --git a/tests/Feature/Controllers/Api/NotificationControllerTest.php b/tests/Feature/Controllers/Api/NotificationControllerTest.php index 4681c8f10..7e8a406fc 100644 --- a/tests/Feature/Controllers/Api/NotificationControllerTest.php +++ b/tests/Feature/Controllers/Api/NotificationControllerTest.php @@ -28,7 +28,6 @@ $user = fn (User $user): array => [ 'id' => $user->id, 'handle' => $user->handle, - 'username' => $user->handle, 'name' => $user->name, 'avatar_url' => $user->avatar_url, 'bio' => $user->bio, diff --git a/tests/Feature/Controllers/Api/RemoteCandidateControllerTest.php b/tests/Feature/Controllers/Api/RemoteCandidateControllerTest.php index 6cf0c1550..951a797b8 100644 --- a/tests/Feature/Controllers/Api/RemoteCandidateControllerTest.php +++ b/tests/Feature/Controllers/Api/RemoteCandidateControllerTest.php @@ -29,7 +29,6 @@ 'user' => [ 'id' => $nuno->id, 'handle' => 'nunomaduro', - 'username' => 'nunomaduro', 'name' => $nuno->name, 'avatar_url' => $nuno->avatar_url, 'bio' => 'Pest and Pint.', diff --git a/tests/Feature/Controllers/Api/SuggestedUserControllerTest.php b/tests/Feature/Controllers/Api/SuggestedUserControllerTest.php index ea4eeb622..a513ec1d7 100644 --- a/tests/Feature/Controllers/Api/SuggestedUserControllerTest.php +++ b/tests/Feature/Controllers/Api/SuggestedUserControllerTest.php @@ -24,7 +24,7 @@ $response->assertOk() ->assertJsonCount(1, 'data') ->assertJsonPath('data.0.id', $vouched->id) - ->assertJsonPath('data.0.username', 'octocat') + ->assertJsonPath('data.0.handle', 'octocat') ->assertJsonPath('data.0.lane', $vouched->lane()) ->assertJsonPath('data.0.followed_by_following_count', 1) ->assertJsonPath('data.0.followers_count', 1) @@ -108,7 +108,7 @@ $profile->following()->attach($followed); - signedApiRequest($this->keyPair, 'GET', route('api-suggested-user.index', ['username' => $profile->handle])) + signedApiRequest($this->keyPair, 'GET', route('api-suggested-user.index', ['handle' => $profile->handle])) ->assertOk() ->assertJsonPath('data.*.id', [$followed->id, $regular->id]); }); @@ -144,7 +144,7 @@ $people->last()->followers()->attach($people->take(3)); [$webRoute, $context] = $page === 'profile' - ? [route('user.show', $author), ['username' => $author->handle]] + ? [route('user.show', $author), ['handle' => $author->handle]] : [route('commit.show', [$author, $root]), ['commit_id' => $root->id]]; $web = null; @@ -188,7 +188,7 @@ $regular = signedApiRequest($this->keyPair, 'GET', route('api-suggested-user.index'))->json('data.*.id'); - signedApiRequest($this->keyPair, 'GET', route('api-suggested-user.index', ['username' => $profile->handle])) + signedApiRequest($this->keyPair, 'GET', route('api-suggested-user.index', ['handle' => $profile->handle])) ->assertOk() ->assertJsonPath('data.*.id', $regular); })->with([ @@ -244,10 +244,10 @@ ->assertUnprocessable() ->assertJsonValidationErrors($field); })->with([ - 'an unknown username' => fn (): array => [['username' => 'nobody-here'], 'username'], + 'an unknown handle' => fn (): array => [['handle' => 'nobody-here'], 'handle'], 'an unknown commit' => fn (): array => [['commit_id' => '01a16c57-e371-73fb-a4d7-6ad455c9dd57'], 'commit_id'], 'a commit id that is not one' => fn (): array => [['commit_id' => 'latest'], 'commit_id'], - 'a profile and a commit' => fn (): array => [['username' => User::factory()->create()->handle, 'commit_id' => Commit::factory()->create()->id], 'commit_id'], + 'a profile and a commit' => fn (): array => [['handle' => User::factory()->create()->handle, 'commit_id' => Commit::factory()->create()->id], 'commit_id'], ]); it('refuses a round that is not a whole number from zero', function (mixed $round): void { diff --git a/tests/Feature/Controllers/Api/SuggestionDismissalControllerTest.php b/tests/Feature/Controllers/Api/SuggestionDismissalControllerTest.php index 8851a07a5..f93da18ef 100644 --- a/tests/Feature/Controllers/Api/SuggestionDismissalControllerTest.php +++ b/tests/Feature/Controllers/Api/SuggestionDismissalControllerTest.php @@ -44,7 +44,7 @@ expect($this->user->dismissedSuggestions()->count())->toBe(0); }); -it('returns not found for unknown usernames', function (): void { +it('returns not found for unknown handles', function (): void { signedApiRequest($this->keyPair, 'POST', 'api/v1/users/missing/dismiss')->assertNotFound(); }); diff --git a/tests/Feature/Controllers/Api/UserControllerTest.php b/tests/Feature/Controllers/Api/UserControllerTest.php index c2a483719..0e34b2482 100644 --- a/tests/Feature/Controllers/Api/UserControllerTest.php +++ b/tests/Feature/Controllers/Api/UserControllerTest.php @@ -33,7 +33,6 @@ ->assertExactJson(['data' => [ 'id' => $user->id, 'handle' => 'octocat', - 'username' => 'octocat', 'name' => 'The Octocat', 'avatar_url' => 'https://avatars.githubusercontent.com/u/583231', 'bio' => 'I am the Octocat.', @@ -103,7 +102,6 @@ 'data' => [ 'id' => $user->id, 'handle' => 'octocat', - 'username' => 'octocat', 'name' => 'The Octocat', 'avatar_url' => 'https://avatars.githubusercontent.com/u/583231', 'bio' => 'I am the Octocat.', diff --git a/tests/Unit/Services/MentionsTest.php b/tests/Unit/Services/MentionsTest.php index 345c5cfd8..43bcc990a 100644 --- a/tests/Unit/Services/MentionsTest.php +++ b/tests/Unit/Services/MentionsTest.php @@ -58,9 +58,9 @@ $mentions = resolve(Mentions::class); expect($mentions->positions('@NunoMaduro and @taylorotwell, thanks! cc @nunomaduro'))->toBe([ - ['handle' => 'NunoMaduro', 'username' => 'NunoMaduro', 'offset' => 0, 'length' => 11], - ['handle' => 'taylorotwell', 'username' => 'taylorotwell', 'offset' => 16, 'length' => 13], - ['handle' => 'nunomaduro', 'username' => 'nunomaduro', 'offset' => 42, 'length' => 11], + ['handle' => 'NunoMaduro', 'offset' => 0, 'length' => 11], + ['handle' => 'taylorotwell', 'offset' => 16, 'length' => 13], + ['handle' => 'nunomaduro', 'offset' => 42, 'length' => 11], ]); }); @@ -68,7 +68,7 @@ $mentions = resolve(Mentions::class); expect($mentions->positions('Café 🎉 with @nuno.'))->toBe([ - ['handle' => 'nuno', 'username' => 'nuno', 'offset' => 12, 'length' => 5], + ['handle' => 'nuno', 'offset' => 12, 'length' => 5], ]); }); @@ -82,8 +82,8 @@ $mentions = resolve(Mentions::class); expect($mentions->positions("thanks @taylorotwell for `@Override`, @nuno\n```java\n@Deprecated\nvoid run() {}\n```\ncc @jess-archer"))->toBe([ - ['handle' => 'taylorotwell', 'username' => 'taylorotwell', 'offset' => 7, 'length' => 13], - ['handle' => 'nuno', 'username' => 'nuno', 'offset' => 38, 'length' => 5], - ['handle' => 'jess-archer', 'username' => 'jess-archer', 'offset' => 85, 'length' => 12], + ['handle' => 'taylorotwell', 'offset' => 7, 'length' => 13], + ['handle' => 'nuno', 'offset' => 38, 'length' => 5], + ['handle' => 'jess-archer', 'offset' => 85, 'length' => 12], ]); });