codetrail-cli 1.4.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- codetrail/__init__.py +3 -0
- codetrail/__main__.py +5 -0
- codetrail/agents/__init__.py +4 -0
- codetrail/agents/convert.py +92 -0
- codetrail/agents/export.py +284 -0
- codetrail/agents/instructions.py +42 -0
- codetrail/agents/protocol.py +111 -0
- codetrail/agents/tools.py +185 -0
- codetrail/assistant/__init__.py +161 -0
- codetrail/assistant/claude_code.py +330 -0
- codetrail/assistant/codex.py +260 -0
- codetrail/assistant/environment.py +66 -0
- codetrail/assistant/estimate.py +225 -0
- codetrail/assistant/fake.py +74 -0
- codetrail/assistant/guard.py +80 -0
- codetrail/assistant/guard_hook.py +58 -0
- codetrail/assistant/local.py +245 -0
- codetrail/assistant/local_tools.py +177 -0
- codetrail/assistant/prompts.py +404 -0
- codetrail/assistant/routing.py +86 -0
- codetrail/assistant/runner.py +120 -0
- codetrail/assistant/status.py +186 -0
- codetrail/assistant/usage.py +93 -0
- codetrail/bridge.py +292 -0
- codetrail/cli.py +407 -0
- codetrail/config.py +485 -0
- codetrail/database/__init__.py +60 -0
- codetrail/database/migrations/0001_facts.sql +34 -0
- codetrail/database/migrations/0002_settings.sql +5 -0
- codetrail/database/migrations/0003_learning.sql +34 -0
- codetrail/database/migrations/0004_assistant_usage.sql +24 -0
- codetrail/database/migrations/0005_page_failures.sql +7 -0
- codetrail/database/migrations/0006_metric_values.sql +9 -0
- codetrail/errors.py +5 -0
- codetrail/extract/__init__.py +249 -0
- codetrail/extract/adr.py +98 -0
- codetrail/extract/github_actions.py +140 -0
- codetrail/extract/openapi.py +101 -0
- codetrail/extract/python.py +221 -0
- codetrail/extract/swift.py +204 -0
- codetrail/extract/terraform.py +166 -0
- codetrail/extract/typescript.py +464 -0
- codetrail/facts/__init__.py +112 -0
- codetrail/facts/store.py +209 -0
- codetrail/generate/__init__.py +1 -0
- codetrail/generate/failures.py +31 -0
- codetrail/generate/outline.py +159 -0
- codetrail/generate/revise.py +72 -0
- codetrail/generate/run.py +530 -0
- codetrail/generate/scope.py +135 -0
- codetrail/generate/validate.py +218 -0
- codetrail/guide/__init__.py +147 -0
- codetrail/learn/__init__.py +143 -0
- codetrail/learn/routes.py +139 -0
- codetrail/locales/ar/LC_MESSAGES/codetrail.mo +0 -0
- codetrail/locales/ar/LC_MESSAGES/codetrail.po +2236 -0
- codetrail/locales/codetrail.pot +1942 -0
- codetrail/lock.py +114 -0
- codetrail/metrics/__init__.py +6 -0
- codetrail/metrics/activity.py +96 -0
- codetrail/metrics/churn.py +156 -0
- codetrail/metrics/commits.py +45 -0
- codetrail/metrics/coverage.py +114 -0
- codetrail/metrics/decisions.py +77 -0
- codetrail/metrics/history.py +47 -0
- codetrail/metrics/inventory.py +63 -0
- codetrail/metrics/rationale.py +74 -0
- codetrail/metrics/releases.py +26 -0
- codetrail/metrics/report.py +111 -0
- codetrail/metrics/repository.py +128 -0
- codetrail/metrics/size.py +95 -0
- codetrail/py.typed +0 -0
- codetrail/remove.py +50 -0
- codetrail/repo/__init__.py +4 -0
- codetrail/repo/git.py +56 -0
- codetrail/repo/gitleaks.toml +6 -0
- codetrail/repo/history.py +262 -0
- codetrail/repo/mirror.py +137 -0
- codetrail/repo/refresh.py +41 -0
- codetrail/repo/rules.py +83 -0
- codetrail/repo/secrets.py +145 -0
- codetrail/repo/signal.py +65 -0
- codetrail/repo/source.py +166 -0
- codetrail/search.py +178 -0
- codetrail/server.py +45 -0
- codetrail/system.py +484 -0
- codetrail/update.py +276 -0
- codetrail/web/__init__.py +1 -0
- codetrail/web/app.py +897 -0
- codetrail/web/diagrams.py +414 -0
- codetrail/web/documentation.py +146 -0
- codetrail/web/i18n.py +44 -0
- codetrail/web/navigation.py +127 -0
- codetrail/web/render.py +126 -0
- codetrail/web/repository_view.py +230 -0
- codetrail/web/security.py +171 -0
- codetrail/web/static/brand/favicon.svg +1 -0
- codetrail/web/static/brand/mark.svg +1 -0
- codetrail/web/static/css/base.css +192 -0
- codetrail/web/static/css/components.css +2254 -0
- codetrail/web/static/css/content.css +310 -0
- codetrail/web/static/css/layout.css +410 -0
- codetrail/web/static/css/print.css +42 -0
- codetrail/web/static/css/tokens.css +228 -0
- codetrail/web/static/js/api.js +49 -0
- codetrail/web/static/js/ask.js +182 -0
- codetrail/web/static/js/d2-worker.js +34 -0
- codetrail/web/static/js/d2.js +28 -0
- codetrail/web/static/js/diagram.js +52 -0
- codetrail/web/static/js/documentation.js +24 -0
- codetrail/web/static/js/learning.js +47 -0
- codetrail/web/static/js/main.js +38 -0
- codetrail/web/static/js/outline.js +44 -0
- codetrail/web/static/js/palette.js +211 -0
- codetrail/web/static/js/repository.js +528 -0
- codetrail/web/static/js/resize.js +126 -0
- codetrail/web/static/js/shortcuts.js +87 -0
- codetrail/web/static/js/sidebar.js +194 -0
- codetrail/web/static/js/theme-init.js +27 -0
- codetrail/web/static/js/theme.js +42 -0
- codetrail/web/static/js/update.js +107 -0
- codetrail/web/static/vendor/d2/LICENSE +375 -0
- codetrail/web/static/vendor/d2/LICENSE-elk +263 -0
- codetrail/web/static/vendor/d2/LICENSE-wasm_exec +27 -0
- codetrail/web/static/vendor/d2/VERSION +12 -0
- codetrail/web/static/vendor/d2/d2.wasm +0 -0
- codetrail/web/static/vendor/d2/elk.js +105914 -0
- codetrail/web/static/vendor/d2/wasm_exec.js +656 -0
- codetrail/web/static/vendor/inter/InterVariable-Italic.woff2 +0 -0
- codetrail/web/static/vendor/inter/InterVariable.woff2 +0 -0
- codetrail/web/static/vendor/inter/LICENSE.txt +92 -0
- codetrail/web/static/vendor/inter/VERSION +6 -0
- codetrail/web/static/vendor/noto-sans-arabic/NotoSansArabic-wght.woff2 +0 -0
- codetrail/web/static/vendor/noto-sans-arabic/OFL.txt +93 -0
- codetrail/web/static/vendor/noto-sans-arabic/VERSION +12 -0
- codetrail/web/target_view.py +63 -0
- codetrail/web/templates/answers.html +25 -0
- codetrail/web/templates/area.html +46 -0
- codetrail/web/templates/base.html +37 -0
- codetrail/web/templates/decisions.html +30 -0
- codetrail/web/templates/diagram.html +10 -0
- codetrail/web/templates/digests.html +24 -0
- codetrail/web/templates/documentation.html +247 -0
- codetrail/web/templates/error.html +9 -0
- codetrail/web/templates/estimate.html +48 -0
- codetrail/web/templates/fact.html +29 -0
- codetrail/web/templates/home.html +196 -0
- codetrail/web/templates/layout/ask_panel.html +27 -0
- codetrail/web/templates/layout/header.html +32 -0
- codetrail/web/templates/layout/icons.html +27 -0
- codetrail/web/templates/layout/outline.html +4 -0
- codetrail/web/templates/layout/palette.html +27 -0
- codetrail/web/templates/layout/shortcuts.html +27 -0
- codetrail/web/templates/layout/sidebar.html +79 -0
- codetrail/web/templates/layout/update_dialog.html +18 -0
- codetrail/web/templates/macros.html +13 -0
- codetrail/web/templates/page.html +93 -0
- codetrail/web/templates/progress.html +46 -0
- codetrail/web/templates/repository.html +369 -0
- codetrail/web/templates/search.html +31 -0
- codetrail/web/templates/source.html +15 -0
- codetrail/web/templates/system.html +43 -0
- codetrail/web/templates/system_arrows.html +17 -0
- codetrail/web/templates/system_legend.html +9 -0
- codetrail/web/templates/update_log.html +30 -0
- codetrail/web/trend.py +22 -0
- codetrail_cli-1.4.1.dist-info/METADATA +372 -0
- codetrail_cli-1.4.1.dist-info/RECORD +171 -0
- codetrail_cli-1.4.1.dist-info/WHEEL +4 -0
- codetrail_cli-1.4.1.dist-info/entry_points.txt +3 -0
- codetrail_cli-1.4.1.dist-info/licenses/LICENSE +202 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{# Small inline icons drawn for Codetrail (design section 16.2). Decorative: hidden from assistive technology. #}
|
|
2
|
+
{% macro icon(name) -%}
|
|
3
|
+
<svg class="icon{% if name == "arrow" %} icon-flip{% endif %}" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
|
|
4
|
+
{%- if name == "search" %}<circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/>
|
|
5
|
+
{%- elif name == "ask" %}<path d="M21 12a8 8 0 0 1-11.6 7.1L4 20l1-4.6A8 8 0 1 1 21 12Z"/>
|
|
6
|
+
{%- elif name == "home" %}<path d="M4 11 12 4l8 7"/><path d="M6 10v10h12V10"/>
|
|
7
|
+
{%- elif name == "progress" %}<path d="M4 20V10"/><path d="M10 20V4"/><path d="M16 20v-7"/><path d="M22 20H2"/>
|
|
8
|
+
{%- elif name == "answer" %}<path d="M6 4h12v16l-6-4-6 4Z"/>
|
|
9
|
+
{%- elif name == "digest" %}<path d="M5 4h14v16H5z"/><path d="M9 9h6M9 13h6"/>
|
|
10
|
+
{%- elif name == "decision" %}<path d="M12 3v18"/><path d="M5 7h14l-2 4H5z"/>
|
|
11
|
+
{%- elif name == "system" %}<rect x="3" y="4" width="7" height="6" rx="1.5"/><rect x="14" y="4" width="7" height="6" rx="1.5"/><rect x="8.5" y="15" width="7" height="6" rx="1.5"/><path d="M10 7h4M17.5 10l-3 5M6.5 10l3 5"/>
|
|
12
|
+
{%- elif name == "metrics" %}<path d="M4 20h16"/><path d="M4 16l5-5 4 3 7-7"/><path d="M16 7h4v4"/>
|
|
13
|
+
{%- elif name == "repository" %}<path d="M4 20V8"/><path d="M9 20V4"/><path d="M14 20v-9"/><path d="M19 20v-5"/><circle cx="19" cy="9" r="2"/>
|
|
14
|
+
{%- elif name == "check" %}<path d="m5 12 4.5 4.5L19 7"/>
|
|
15
|
+
{%- elif name == "sun" %}<circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/>
|
|
16
|
+
{%- elif name == "menu" %}<path d="M4 7h16M4 12h16M4 17h16"/>
|
|
17
|
+
{%- elif name == "close" %}<path d="M6 6l12 12M18 6 6 18"/>
|
|
18
|
+
{%- elif name == "sidebar" %}<rect x="3" y="4" width="18" height="16" rx="3"/><path d="M9 4v16"/>
|
|
19
|
+
{%- elif name == "pin" %}<path d="M9 4h6l-1 6 3 3H7l3-3-1-6Z"/><path d="M12 13v7"/>
|
|
20
|
+
{%- elif name == "chevron" %}<path d="m9 6 6 6-6 6"/>
|
|
21
|
+
{%- elif name == "keyboard" %}<rect x="2" y="6" width="20" height="12" rx="2"/><path d="M6 10h.01M10 10h.01M14 10h.01M18 10h.01M7 14h10"/>
|
|
22
|
+
{%- elif name == "arrow" %}<path d="M5 12h14M13 6l6 6-6 6"/>
|
|
23
|
+
{%- elif name == "expand" %}<path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/>
|
|
24
|
+
{%- elif name == "folder" %}<path d="M3 6h7l2 2h9v11H3z"/>
|
|
25
|
+
{%- endif -%}
|
|
26
|
+
</svg>
|
|
27
|
+
{%- endmacro %}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{% from "layout/icons.html" import icon %}
|
|
2
|
+
{% from "macros.html" import kind_name %}
|
|
3
|
+
<dialog class="palette" data-palette aria-label="{{ _('Search or ask') }}"
|
|
4
|
+
{% for kind in ["area", "concept", "path", "digest", "answer", "decision", "fact"] %}data-kind-{{ kind }}="{{ kind_name(kind) }}" {% endfor %}
|
|
5
|
+
data-group-pages="{{ _('Pages') }}" data-group-answers="{{ _('Saved answers') }}"
|
|
6
|
+
data-group-decisions="{{ _('Decisions') }}" data-group-facts="{{ _('Facts') }}"
|
|
7
|
+
data-group-actions="{{ _('Actions') }}"
|
|
8
|
+
data-action-ask="{{ _('Ask about: “{query}”') }}" data-action-all="{{ _('See every result for “{query}”') }}"
|
|
9
|
+
data-action-update="{{ _('Update the guide…') }}" data-action-progress="{{ _('Go to progress') }}"
|
|
10
|
+
data-action-theme="{{ _('Switch theme') }}"
|
|
11
|
+
data-label-searching="{{ _('Searching…') }}" data-label-empty="{{ _('Nothing in the guide matches “{query}”.') }}"
|
|
12
|
+
data-label-unavailable="{{ _('Search isn’t available right now.') }}">
|
|
13
|
+
<div class="palette-field">
|
|
14
|
+
{{ icon("search") }}
|
|
15
|
+
<input type="text" data-palette-input role="combobox" aria-expanded="false" aria-controls="palette-list"
|
|
16
|
+
aria-autocomplete="list" aria-label="{{ _('Search the guide, or ask a question') }}"
|
|
17
|
+
placeholder="{{ _('Search the guide, or ask a question…') }}" autocomplete="off" spellcheck="false" maxlength="1000">
|
|
18
|
+
<kbd>Esc</kbd>
|
|
19
|
+
</div>
|
|
20
|
+
<ul class="palette-list" id="palette-list" role="listbox" data-palette-list aria-label="{{ _('Results') }}"></ul>
|
|
21
|
+
<p class="palette-status" data-palette-status aria-live="polite"></p>
|
|
22
|
+
<div class="palette-footer" aria-hidden="true">
|
|
23
|
+
<span><kbd>↑</kbd> <kbd>↓</kbd> {{ _("to move") }}</span>
|
|
24
|
+
<span><kbd>Enter</kbd> {{ _("to open") }}</span>
|
|
25
|
+
<span><kbd>Esc</kbd> {{ _("to close") }}</span>
|
|
26
|
+
</div>
|
|
27
|
+
</dialog>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<dialog class="shortcuts-dialog" data-shortcuts aria-labelledby="shortcuts-title">
|
|
2
|
+
<div class="dialog-body">
|
|
3
|
+
<h2 id="shortcuts-title">{{ _("Keyboard shortcuts") }}</h2>
|
|
4
|
+
<table>
|
|
5
|
+
<tbody>
|
|
6
|
+
<tr><td><kbd>⌘</kbd> <kbd>K</kbd> · <kbd>/</kbd></td><td>{{ _("Search, or ask a question") }}</td></tr>
|
|
7
|
+
<tr><td><kbd>A</kbd></td><td>{{ _("Ask about this page") }}</td></tr>
|
|
8
|
+
<tr><td><kbd>G</kbd> <kbd>H</kbd></td><td>{{ _("Go home") }}</td></tr>
|
|
9
|
+
<tr><td><kbd>G</kbd> <kbd>P</kbd></td><td>{{ _("Go to your progress") }}</td></tr>
|
|
10
|
+
<tr><td><kbd>G</kbd> <kbd>Y</kbd></td><td>{{ _("Go to your system") }}</td></tr>
|
|
11
|
+
<tr><td><kbd>G</kbd> <kbd>S</kbd></td><td>{{ _("Go to saved answers") }}</td></tr>
|
|
12
|
+
<tr><td><kbd>G</kbd> <kbd>D</kbd></td><td>{{ _("Go to digests") }}</td></tr>
|
|
13
|
+
<tr><td><kbd>G</kbd> <kbd>R</kbd></td><td>{{ _("Go to decisions") }}</td></tr>
|
|
14
|
+
<tr><td><kbd>G</kbd> <kbd>O</kbd></td><td>{{ _("Go to documentation") }}</td></tr>
|
|
15
|
+
<tr><td><kbd>G</kbd> <kbd>T</kbd></td><td>{{ _("Go to the repository") }}</td></tr>
|
|
16
|
+
<tr><td><kbd>[</kbd> · <kbd>]</kbd></td><td>{{ _("Previous or next page in the path") }}</td></tr>
|
|
17
|
+
<tr><td><kbd>\</kbd></td><td>{{ _("Hide or show the sidebar") }}</td></tr>
|
|
18
|
+
<tr><td><kbd>M</kbd></td><td>{{ _("Mark this page read, or unread") }}</td></tr>
|
|
19
|
+
<tr><td><kbd>U</kbd></td><td>{{ _("Update the guide (shows the estimate first)") }}</td></tr>
|
|
20
|
+
<tr><td><kbd>?</kbd></td><td>{{ _("Show these shortcuts") }}</td></tr>
|
|
21
|
+
<tr><td><kbd>Esc</kbd></td><td>{{ _("Close the palette, the panel, the sidebar's peek or a dialog") }}</td></tr>
|
|
22
|
+
</tbody>
|
|
23
|
+
</table>
|
|
24
|
+
<p class="faint">{{ _("No shortcut spends anything: questions are sent by their Send button, and updates start only when you choose Go ahead.") }}</p>
|
|
25
|
+
</div>
|
|
26
|
+
<div class="dialog-actions"><button type="button" class="button" data-shortcuts-close>{{ _("Close") }}</button></div>
|
|
27
|
+
</dialog>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{% from "layout/icons.html" import icon %}
|
|
2
|
+
{# The sidebar (design section 22): pinned tiles, then the guide's lists in folding sections. Folding, hiding and
|
|
3
|
+
pinning are run by sidebar.js; without it every section is open and the tiles still link. #}
|
|
4
|
+
{% set pinned_ids = nav.pins|map(attribute="id")|list %}
|
|
5
|
+
{% set full = pinned_ids|length >= max_pins %}
|
|
6
|
+
{% macro fold(name, heading, count=none) -%}
|
|
7
|
+
<h2 class="nav-heading"><button type="button" class="nav-fold" data-fold="{{ name }}" aria-expanded="true" aria-controls="nav-{{ name }}">{{ icon("chevron") }}<span class="nav-fold-label">{{ heading }}</span>{% if count is not none %}<span class="count">{{ count }}</span>{% endif %}</button></h2>
|
|
8
|
+
{%- endmacro %}
|
|
9
|
+
{% macro pin(page) -%}
|
|
10
|
+
<button type="button" class="nav-pin" data-pin="{{ page.id }}" data-kind="{{ page.kind }}" data-initials="{{ page.title|initials }}" aria-pressed="{{ 'true' if page.id in pinned_ids else 'false' }}" aria-label="{{ _('Pin %(title)s', title=page.title) }}"{% if full and page.id not in pinned_ids %} disabled title="{{ _('Unpin a page first') }}"{% endif %} hidden>{{ icon("pin") }}</button>
|
|
11
|
+
{%- endmacro %}
|
|
12
|
+
<nav class="sidebar" id="sidebar" aria-label="{{ _('Guide') }}" data-max-pins="{{ max_pins }}"
|
|
13
|
+
data-label-unpin="{{ _('Unpin %(title)s', title='{title}') }}" data-label-full="{{ _('Unpin a page first') }}">
|
|
14
|
+
<ul class="pins" data-pins aria-label="{{ _('Pinned pages') }}"{% if not nav.pins %} hidden{% endif %}>
|
|
15
|
+
{% for page in nav.pins %}<li class="pin-item"><a class="pin" href="/pages/{{ page.id }}" {% if active == page.id %}aria-current="page" {% endif %}title="{{ page.title }}" aria-label="{{ page.title }}" {% if page.kind == "answer" %}dir="auto"{% else %}lang="en" dir="ltr"{% endif %}>{{ page.title|initials }}</a><button type="button" class="pin-remove" data-unpin="{{ page.id }}" aria-label="{{ _('Unpin %(title)s', title=page.title) }}" hidden>{{ icon("close") }}</button></li>{% endfor %}
|
|
16
|
+
</ul>
|
|
17
|
+
<template data-pin-template><li class="pin-item"><a class="pin"></a><button type="button" class="pin-remove">{{ icon("close") }}</button></li></template>
|
|
18
|
+
|
|
19
|
+
<ul class="nav-list">
|
|
20
|
+
<li><a class="nav-link" href="/" {% if active == "home" %}aria-current="page"{% endif %}>{{ icon("home") }}<span class="label">{{ _("Home") }}</span></a></li>
|
|
21
|
+
<li><a class="nav-link" href="/progress" {% if active == "progress" %}aria-current="page"{% endif %}>{{ icon("progress") }}<span class="label">{{ _("Progress") }}</span></a></li>
|
|
22
|
+
{% if has_system %}<li><a class="nav-link" href="/system" {% if active == "system" %}aria-current="page"{% endif %}>{{ icon("system") }}<span class="label">{{ _("System") }}</span></a></li>{% endif %}
|
|
23
|
+
</ul>
|
|
24
|
+
|
|
25
|
+
{% if nav.paths %}
|
|
26
|
+
<div class="nav-section" data-section="paths">
|
|
27
|
+
{{ fold("paths", _("Paths"), nav.paths|length) }}
|
|
28
|
+
<ul class="nav-list" id="nav-paths" lang="en" dir="ltr">
|
|
29
|
+
{% for path, learned, total in nav.paths %}
|
|
30
|
+
<li class="nav-row"><a class="nav-link nav-path" href="/pages/{{ path.id }}" {% if active == path.id %}aria-current="page"{% endif %}>
|
|
31
|
+
<span class="label">{{ path.title }}<progress class="bar" max="{{ total or 1 }}" value="{{ learned }}" aria-hidden="true"></progress></span>
|
|
32
|
+
<span class="count">{{ learned }}/{{ total }}</span></a>{{ pin(path) }}</li>
|
|
33
|
+
{% endfor %}
|
|
34
|
+
</ul>
|
|
35
|
+
</div>
|
|
36
|
+
{% endif %}
|
|
37
|
+
|
|
38
|
+
{% for name, heading, pages in [("areas", _("Areas"), nav.areas), ("concepts", _("Concepts"), nav.concepts)] %}
|
|
39
|
+
{% if pages %}
|
|
40
|
+
<div class="nav-section" data-section="{{ name }}">
|
|
41
|
+
{{ fold(name, heading, pages|length) }}
|
|
42
|
+
<ul class="nav-list" id="nav-{{ name }}" lang="en" dir="ltr">
|
|
43
|
+
{% for page in pages %}
|
|
44
|
+
<li class="nav-row"><a class="nav-link" href="/pages/{{ page.id }}" {% if active == page.id %}aria-current="page"{% endif %}><span class="label">{{ page.title }}</span>{% if nav.statuses.get(page.id) == "learned" %}<span class="check" title="{{ _('Learned') }}">{{ icon("check") }}</span>{% elif nav.statuses.get(page.id) == "stale" %}<span class="dot" title="{{ _('Changed since you learned it') }}"></span>{% endif %}</a>{{ pin(page) }}</li>
|
|
45
|
+
{% endfor %}
|
|
46
|
+
</ul>
|
|
47
|
+
</div>
|
|
48
|
+
{% endif %}
|
|
49
|
+
{% endfor %}
|
|
50
|
+
|
|
51
|
+
<div class="nav-section" data-section="answers">
|
|
52
|
+
{{ fold("answers", _("Saved answers"), nav.answer_count) }}
|
|
53
|
+
<ul class="nav-list" id="nav-answers" data-nav-answers>
|
|
54
|
+
{% for answer in nav.answers %}
|
|
55
|
+
<li class="nav-row"><a class="nav-link" href="/pages/{{ answer.id }}" {% if active == answer.id %}aria-current="page"{% endif %}><span class="label" dir="auto">{{ answer.title }}</span></a>{{ pin(answer) }}</li>
|
|
56
|
+
{% else %}
|
|
57
|
+
<li class="nav-empty" data-nav-empty>{{ _("Answers you save appear here.") }}</li>
|
|
58
|
+
{% endfor %}
|
|
59
|
+
{% if nav.answer_count %}
|
|
60
|
+
<li><a class="nav-link" href="/answers" {% if active == "answers" %}aria-current="page"{% endif %}><span class="label">{{ _("All saved answers") }}</span><span class="count">{{ nav.answer_count }}</span></a></li>
|
|
61
|
+
{% endif %}
|
|
62
|
+
</ul>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<div class="nav-section" data-section="more">
|
|
66
|
+
{{ fold("more", _("More")) }}
|
|
67
|
+
<ul class="nav-list" id="nav-more">
|
|
68
|
+
<li><a class="nav-link" href="/digests" {% if active == "digests" %}aria-current="page"{% endif %}>{{ icon("digest") }}<span class="label">{{ _("Digests") }}</span>{% if nav.unread_digests %}<span class="dot" title="{{ _('Unread') }}"></span>{% endif %}</a></li>
|
|
69
|
+
<li><a class="nav-link" href="/decisions" {% if active == "decisions" %}aria-current="page"{% endif %}>{{ icon("decision") }}<span class="label">{{ _("Decisions") }}</span></a></li>
|
|
70
|
+
<li><a class="nav-link" href="/documentation" {% if active == "documentation" %}aria-current="page"{% endif %}>{{ icon("metrics") }}<span class="label">{{ _("Documentation") }}</span></a></li>
|
|
71
|
+
<li><a class="nav-link" href="/repository" {% if active == "repository" %}aria-current="page"{% endif %}>{{ icon("repository") }}<span class="label">{{ _("Repository") }}</span></a></li>
|
|
72
|
+
</ul>
|
|
73
|
+
</div>
|
|
74
|
+
</nav>
|
|
75
|
+
<div class="resize-handle resize-sidebar" data-resize="sidebar" role="separator" aria-orientation="vertical"
|
|
76
|
+
aria-controls="sidebar" aria-label="{{ _('Resize the sidebar') }}" title="{{ _('Drag to resize; double-click to reset') }}"
|
|
77
|
+
tabindex="0"></div>
|
|
78
|
+
<button type="button" class="sidebar-edge" data-sidebar-edge aria-controls="sidebar" aria-expanded="false"
|
|
79
|
+
aria-label="{{ _('Show the sidebar for a moment') }}"></button>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<dialog class="estimate-dialog" data-estimate-dialog aria-labelledby="estimate-title">
|
|
2
|
+
<div class="dialog-body" data-estimate-body></div>
|
|
3
|
+
<div class="dialog-actions">
|
|
4
|
+
<button type="button" class="button" data-estimate-cancel>{{ _("Cancel") }}</button>
|
|
5
|
+
<button type="button" class="button primary" data-estimate-go>{{ _("Go ahead") }}</button>
|
|
6
|
+
</div>
|
|
7
|
+
</dialog>
|
|
8
|
+
{% from "layout/icons.html" import icon %}
|
|
9
|
+
<aside class="update-panel" data-update-panel aria-labelledby="update-title" hidden
|
|
10
|
+
data-label-running="{{ _('Updating…') }}" data-label-done="{{ _('Updated.') }}"
|
|
11
|
+
data-label-failed="{{ _('The update failed:') }}">
|
|
12
|
+
<div class="ask-head">
|
|
13
|
+
<h2 id="update-title">{{ _("Updating the guide") }}</h2>
|
|
14
|
+
<button type="button" class="icon-button" data-update-close aria-label="{{ _('Close') }}">{{ icon("close") }}</button>
|
|
15
|
+
</div>
|
|
16
|
+
<ol class="update-log" data-update-log aria-live="polite"></ol>
|
|
17
|
+
<p class="update-state" role="status" data-update-state></p>
|
|
18
|
+
</aside>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{# A call's estimate on its button (design section 15.4): tokens, and its cost at API prices when known. #}
|
|
2
|
+
{% macro cost(estimate) -%}
|
|
3
|
+
{%- if estimate -%}
|
|
4
|
+
<span class="cost" dir="ltr" title="{{ _('An estimate. On a subscription this counts against your plan’s usage limits; with an API key it is billed at these prices.') }}">~{{ estimate.tokens|tokens }} {{ _("tokens") }}{% if estimate.cost_usd is none %}{% elif estimate.cost_usd == 0 %} · {{ _("free") }}{% else %} · ~{{ estimate.cost_usd|dollars }}{% endif %}</span>
|
|
5
|
+
{%- endif -%}
|
|
6
|
+
{%- endmacro %}
|
|
7
|
+
|
|
8
|
+
{# A search result's kind, in the reader's language (design section 7.2). #}
|
|
9
|
+
{% macro kind_name(kind) -%}
|
|
10
|
+
{%- set names = {"area": _("Area"), "concept": _("Concept"), "path": _("Path"), "digest": _("Digest"),
|
|
11
|
+
"answer": _("Saved answer"), "decision": _("Decision"), "fact": _("Fact")} -%}
|
|
12
|
+
{{ names.get(kind, kind) }}
|
|
13
|
+
{%- endmacro %}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
{% extends "base.html" %}
|
|
2
|
+
{% from "layout/icons.html" import icon %}
|
|
3
|
+
{% from "macros.html" import cost with context %}
|
|
4
|
+
{% block title %}{{ page.title }}{% endblock %}
|
|
5
|
+
{% block content %}
|
|
6
|
+
<div class="reading">
|
|
7
|
+
<nav aria-label="{{ _('Breadcrumbs') }}">
|
|
8
|
+
<ol class="breadcrumbs">
|
|
9
|
+
<li><a href="/">{{ _("Home") }}</a></li>
|
|
10
|
+
{% if in_path %}<li><a href="/pages/{{ in_path.id }}" lang="en" dir="ltr">{{ in_path.title }}</a></li>{% endif %}
|
|
11
|
+
<li>{% if page.kind == "area" %}{{ _("Area") }}{% elif page.kind == "concept" %}{{ _("Concept") }}{% elif page.kind == "path" %}{{ _("Path") }}{% elif page.kind == "digest" %}{{ _("Digest") }}{% elif page.kind == "answer" %}<a href="/answers">{{ _("Saved answers") }}</a>{% endif %}</li>
|
|
12
|
+
</ol>
|
|
13
|
+
</nav>
|
|
14
|
+
<article class="guide-page" lang="en" dir="ltr">
|
|
15
|
+
<h1 {% if page.kind == "answer" %}dir="auto"{% endif %}>{{ page.title }}</h1>
|
|
16
|
+
{% if page.kind == "digest" %}
|
|
17
|
+
<p class="faint">{% if page.meta.from_commit %}{{ page.meta.from_commit[:12] }} → {% endif %}{{ page.meta.to_commit[:12] }}</p>
|
|
18
|
+
{% elif page.kind == "answer" %}
|
|
19
|
+
<p class="faint" lang="{{ language.code }}" dir="{{ language.direction }}">{% trans when=(page.meta.get("asked_at") or "")[:10], commit=(page.meta.get("built_at") or "")[:12] %}Saved answer, asked {{ when }} at commit {{ commit }}.{% endtrans %}</p>
|
|
20
|
+
{% endif %}
|
|
21
|
+
<div class="page-status" lang="{{ language.code }}" dir="{{ language.direction }}">
|
|
22
|
+
{% if page.kind not in ("digest", "answer", "path") %}
|
|
23
|
+
<span class="pill pill-{{ status }}" data-status="{{ status }}">{% if status == "learned" %}{{ icon("check") }} {{ _("Learned") }}{% elif status == "stale" %}{{ _("Learned, but changed since") }}{% elif status == "read" %}{{ _("Read") }}{% else %}{{ _("Not read yet") }}{% endif %}</span>
|
|
24
|
+
{% if status == "unread" %}<button type="button" class="button small" data-mark-read="{{ page.id }}">{{ _("Mark read") }} <kbd>M</kbd></button>
|
|
25
|
+
{% elif status == "read" %}<button type="button" class="button small" data-mark-unread="{{ page.id }}">{{ _("Mark unread") }} <kbd>M</kbd></button>{% endif %}
|
|
26
|
+
{% else %}
|
|
27
|
+
<span class="pill" data-status="{{ status }}">{% if status == "read" %}{{ _("Read") }}{% else %}{{ _("Not read yet") }}{% endif %}</span>
|
|
28
|
+
{% if status == "unread" and page.kind == "digest" %}<button type="button" class="button small" data-mark-read="{{ page.id }}">{{ _("Mark read") }} <kbd>M</kbd></button>{% endif %}
|
|
29
|
+
{% endif %}
|
|
30
|
+
<button type="button" class="button small" data-ask-open>{{ icon("ask") }} {{ _("Ask about this page") }} <kbd>A</kbd></button>
|
|
31
|
+
</div>
|
|
32
|
+
{% if changes %}
|
|
33
|
+
<details class="changes" open lang="{{ language.code }}" dir="{{ language.direction }}">
|
|
34
|
+
<summary>{{ _("What changed since you learned this page") }}</summary>
|
|
35
|
+
<pre lang="en" dir="ltr">{% for line in changes.splitlines() %}<span class="{% if line.startswith('+') %}added{% elif line.startswith('-') %}removed{% endif %}">{{ line }}</span>
|
|
36
|
+
{% endfor %}</pre>
|
|
37
|
+
</details>
|
|
38
|
+
{% endif %}
|
|
39
|
+
{% if sources_changed %}
|
|
40
|
+
<p class="notice" lang="{{ language.code }}" dir="{{ language.direction }}">{% if page.kind == "answer" %}{{ _("Files this answer was written from have changed since. Saved answers aren't rewritten; ask again for a fresh one.") }}{% else %}{{ _("Files this page was written from have changed since. It will be checked again at the next update.") }}{% endif %}</p>
|
|
41
|
+
{% endif %}
|
|
42
|
+
<div class="prose">
|
|
43
|
+
{% for segment in segments %}
|
|
44
|
+
{% if segment.kind == "html" %}
|
|
45
|
+
{{ segment.html }}
|
|
46
|
+
{% elif segment.kind == "rationale" %}
|
|
47
|
+
<aside class="rationale {{ segment.label }}">
|
|
48
|
+
<p class="rationale-label">
|
|
49
|
+
{% if segment.label == "documented" %}Documented{% if segment.citation %} · {% if segment.link %}<a href="{{ segment.link }}">{{ segment.citation }}</a>{% else %}{{ segment.citation }}{% endif %}{% endif %}
|
|
50
|
+
{% else %}Inferred · the assistant's reading of the code{% endif %}
|
|
51
|
+
</p>
|
|
52
|
+
{{ segment.html }}
|
|
53
|
+
</aside>
|
|
54
|
+
{% elif segment.kind == "diagram" %}
|
|
55
|
+
{% with diagram=segment.diagram %}{% include "diagram.html" %}{% endwith %}
|
|
56
|
+
{% endif %}
|
|
57
|
+
{% endfor %}
|
|
58
|
+
</div>
|
|
59
|
+
{% if steps %}
|
|
60
|
+
<h2 lang="{{ language.code }}" dir="{{ language.direction }}">{{ _("Steps") }}</h2>
|
|
61
|
+
<ol class="list" lang="en" dir="ltr">
|
|
62
|
+
{% for step in steps %}
|
|
63
|
+
<li><a class="grow" href="/pages/{{ step }}?path={{ page.id }}">{{ titles.get(step, step) }}</a>{% if nav.statuses.get(step) == "learned" %}<span class="pill pill-learned">{{ icon("check") }}</span>{% endif %}</li>
|
|
64
|
+
{% endfor %}
|
|
65
|
+
</ol>
|
|
66
|
+
{% endif %}
|
|
67
|
+
</article>
|
|
68
|
+
{% if checks %}
|
|
69
|
+
<section class="checks" lang="{{ language.code }}" dir="{{ language.direction }}"
|
|
70
|
+
data-label-pass="{{ _('Passed') }}" data-label-partial="{{ _('Partly there') }}"
|
|
71
|
+
data-label-fail="{{ _('Not yet') }}" data-label-ungraded="{{ _('The answer couldn’t be graded.') }}">
|
|
72
|
+
<h2>{{ _("Check your understanding") }}</h2>
|
|
73
|
+
{% for check in checks %}
|
|
74
|
+
<form class="check" data-check="{{ check.id }}" data-page="{{ page.id }}">
|
|
75
|
+
<p class="check-question" lang="en" dir="ltr"><span>{{ check.question }}</span>{% if check.passed %}<span class="pill pill-learned">{{ icon("check") }} {{ _("Passed") }}</span>{% endif %}</p>
|
|
76
|
+
<label class="visually-hidden" for="answer-{{ check.id }}">{{ _("Your answer") }}</label>
|
|
77
|
+
<textarea id="answer-{{ check.id }}" name="answer" rows="3" maxlength="4000" required></textarea>
|
|
78
|
+
<div class="check-row">
|
|
79
|
+
<button type="submit" class="button">{{ _("Check my answer") }} {{ cost(estimates.get("grade")) }}</button>
|
|
80
|
+
<p class="feedback" data-feedback dir="auto" aria-live="polite"></p>
|
|
81
|
+
</div>
|
|
82
|
+
</form>
|
|
83
|
+
{% endfor %}
|
|
84
|
+
</section>
|
|
85
|
+
{% endif %}
|
|
86
|
+
{% if previous or following %}
|
|
87
|
+
<nav class="pager" aria-label="{{ _('In this path') }}">
|
|
88
|
+
{% if previous %}<a class="previous" href="/pages/{{ previous.id }}?path={{ in_path.id }}" data-pager-previous><small>{{ _("Previous") }} <kbd>[</kbd></small><span lang="en" dir="ltr">{{ previous.title }}</span></a>{% endif %}
|
|
89
|
+
{% if following %}<a class="next" href="/pages/{{ following.id }}?path={{ in_path.id }}" data-pager-next><small>{{ _("Next") }} <kbd>]</kbd></small><span lang="en" dir="ltr">{{ following.title }}</span></a>{% endif %}
|
|
90
|
+
</nav>
|
|
91
|
+
{% endif %}
|
|
92
|
+
</div>
|
|
93
|
+
{% endblock %}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{% extends "base.html" %}
|
|
2
|
+
{% from "layout/icons.html" import icon %}
|
|
3
|
+
{% block title %}{{ _("Progress") }}{% endblock %}
|
|
4
|
+
{% block content %}
|
|
5
|
+
<div class="wide">
|
|
6
|
+
<header class="page-head">
|
|
7
|
+
<h1>{{ _("Your progress") }}</h1>
|
|
8
|
+
<p class="muted">{{ _("A page is learned when you pass its checks. When an update changes a page you learned, it shows here until you learn it again.") }}</p>
|
|
9
|
+
</header>
|
|
10
|
+
{% if not paths_steps and not unpathed %}
|
|
11
|
+
<section class="empty">
|
|
12
|
+
<h2>{{ _("Nothing to learn yet") }}</h2>
|
|
13
|
+
<p>{{ _("Update the guide and its guided paths appear here, with what you've learned on each.") }}</p>
|
|
14
|
+
<button type="button" class="button primary" data-update-button>{{ _("Update the guide") }}</button>
|
|
15
|
+
</section>
|
|
16
|
+
{% endif %}
|
|
17
|
+
{% for path, learned, total, steps in paths_steps %}
|
|
18
|
+
<section aria-labelledby="path-{{ loop.index }}">
|
|
19
|
+
<div class="section-head">
|
|
20
|
+
<h2 id="path-{{ loop.index }}" lang="en" dir="ltr"><a href="/pages/{{ path.id }}">{{ path.title }}</a></h2>
|
|
21
|
+
<span class="faint">{% trans learned=learned, total=total %}{{ learned }} of {{ total }} learned{% endtrans %}</span>
|
|
22
|
+
</div>
|
|
23
|
+
<progress class="bar" max="{{ total or 1 }}" value="{{ learned }}" aria-labelledby="path-{{ loop.index }}"></progress>
|
|
24
|
+
<ol class="list spaced" lang="en" dir="ltr">
|
|
25
|
+
{% for step in steps %}
|
|
26
|
+
{% set state = nav.statuses.get(step.id, "unread") %}
|
|
27
|
+
<li><a class="grow" href="/pages/{{ step.id }}?path={{ path.id }}">{{ step.title }}</a>
|
|
28
|
+
<span class="pill pill-{{ state }}" lang="{{ language.code }}" dir="{{ language.direction }}">{% if state == "learned" %}{{ icon("check") }} {{ _("Learned") }}{% elif state == "stale" %}{{ _("Changed since you learned it") }}{% elif state == "read" %}{{ _("Read") }}{% else %}{{ _("Not read yet") }}{% endif %}</span></li>
|
|
29
|
+
{% endfor %}
|
|
30
|
+
</ol>
|
|
31
|
+
</section>
|
|
32
|
+
{% endfor %}
|
|
33
|
+
{% if unpathed %}
|
|
34
|
+
<section>
|
|
35
|
+
<h2>{{ _("Outside the paths") }}</h2>
|
|
36
|
+
<ul class="list" lang="en" dir="ltr">
|
|
37
|
+
{% for page in unpathed %}
|
|
38
|
+
{% set state = nav.statuses.get(page.id, "unread") %}
|
|
39
|
+
<li><a class="grow" href="/pages/{{ page.id }}">{{ page.title }}</a>
|
|
40
|
+
<span class="pill pill-{{ state }}" lang="{{ language.code }}" dir="{{ language.direction }}">{% if state == "learned" %}{{ icon("check") }} {{ _("Learned") }}{% elif state == "stale" %}{{ _("Changed since you learned it") }}{% elif state == "read" %}{{ _("Read") }}{% else %}{{ _("Not read yet") }}{% endif %}</span></li>
|
|
41
|
+
{% endfor %}
|
|
42
|
+
</ul>
|
|
43
|
+
</section>
|
|
44
|
+
{% endif %}
|
|
45
|
+
</div>
|
|
46
|
+
{% endblock %}
|