Skip to content

feat(project-configuration): add read-only Project configuration page - #3167

Open
mettta wants to merge 7 commits into
mainfrom
mettta/feat_configuration_page
Open

mettta wants to merge 7 commits into
mainfrom
mettta/feat_configuration_page

Conversation

@mettta

@mettta mettta commented Sep 1, 2026 •

Copy link
Copy Markdown
Collaborator

Add a read-only Project configuration page (server and static output),
linked from the shared navigation. Shows the active config file path,
project title, server host/port, project features, and additional settings
(lazy-loading threshold, output dir, grammar aliases, custom CSS/favicon paths).

The "Project tree configuration" block (input paths, included/excluded
document and source paths, source root path) moves here from the project
index page. Config-declared excludes and .gitignore-derived excludes now
render separately, the latter collapsed by default under
"Excluded via .gitignore (N)".

Also included, as self-contained commits:

  • fix(core/project_config): ALL_FEATURES now expands correctly when
    project_features is assigned after construction (extending configs),
    not only via the constructor argument. Affects feature gating
    project-wide, not just this page.
  • feat(export/html): bordered, uppercase <kbd> hint style (also picked up
    by tree_map).
  • feat(modal): the shared modal footer now puts Cancel before Submit,
    affecting every modal built on this component.

See developer/tasks/20260821_feat_UI_settings/task.md for the full spec.

image

@mettta
mettta requested a review from stanislaw September 1, 2026 00:46
Comment thread developer/tasks/20260821_feat_UI_settings/task.md Outdated
Comment thread strictdoc/core/project_config.py Outdated
Comment thread strictdoc/core/project_config.py Outdated
Comment thread strictdoc/core/project_config.py Outdated
@@ -0,0 +1,26 @@
(() => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add a small top-level comment to explain what this file is about.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@@ -0,0 +1,219 @@
(() => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's move this to the feature's folder.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment thread strictdoc/server/routers/main_router.py Outdated
Comment thread strictdoc/export/html/templates/actions/project_settings/modal.jinja Outdated
{% macro value_or_none(value_) %}{{ value_ if value_ is not none and value_ != "" else "None" }}{% endmacro %}
{% macro enabled(value_) %}{{ "On" if value_ else "Off" }}{% endmacro %}
{% macro list_value(values_) %}{{ values_ | join(", ") if values_ else "None" }}{% endmacro %}
{% macro features_value(values_) %}{% if "ALL_FEATURES" in values_ %}<b>ALL_FEATURES:</b><br/> {{ list_value(values_ | reject("equalto", "ALL_FEATURES") | list) }}{% else %}{{ list_value(values_) }}{% endif %}{% endmacro %}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not use any macro. Use methods on the view object. Encapsulate everything there.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment thread strictdoc/features/project_configuration/__init__.py Outdated
Comment thread .gitignore Outdated
@mettta
mettta force-pushed the mettta/feat_configuration_page branch from fe986fe to d359e7e Compare September 15, 2026 17:55
@stanislaw
stanislaw marked this pull request as draft September 15, 2026 18:13
…s reassigned after construction

ProjectConfig.__init__ expands the ALL_FEATURES sentinel into the full
ProjectFeature list, but it only did this when project_features was passed
as a constructor argument. It is equally common, and fully supported by
the documented create_config() mechanism, to write:

    def create_config() -> ProjectConfig:
        config = ProjectConfig()
        config.project_features = ["ALL_FEATURES", "SEARCH"]
        return config

This is a plain attribute assignment on an already-constructed object, so
the expansion logic inside __init__ never runs for it. project_features
was then left holding the literal list from the source file (the
ALL_FEATURES sentinel plus whatever else was assigned), instead of the
full, expanded feature list.

self.project_features is read throughout StrictDoc, not just in one place:
is_activated_diff(), is_activated_search(), is_activated_html2pdf(),
is_feature_activated(), the nav template, the MATHJAX/MERMAID deprecation
warnings in validate_and_finalize(), and the CLI export path. Any of these
could silently see the wrong feature set for a project using this
attribute-assignment style, with no error raised anywhere.

Factor the validation-and-expansion logic out of __init__ into a
ProjectConfig._normalize_project_features() static method, and call it
from both __init__ (unchanged behavior for the constructor-argument case)
and from validate_and_finalize() (new: covers the attribute-assignment
case). validate_and_finalize() runs at the end of every
ProjectConfigLoader.load*() path, so this fires regardless of how
project_features ended up set. The call is idempotent: an already-expanded
list still contains ALL_FEATURES (it is one of the ProjectFeature enum
members), so re-normalizing it a second time is a no-op.

Add two tests covering both paths end to end: constructing with
project_features=["ALL_FEATURES", ...] directly, and loading a config file
whose create_config() assigns project_features to an already-constructed
ProjectConfig. Both assert the final project_features equals
ProjectFeature.all(), not just that the source text was written a
particular way.
Add a dedicated Project configuration page, linked from the shared
navigation. StrictDoc generates it identically in server and static HTML
output: the same read-only configuration information either way, no
editing controls in either mode.

The page shows:
- the active configuration file path, project title, server host and port;
- lazy document loading threshold (with the installed version's default
  shown alongside it), output directory, grammar aliases, custom CSS path,
  and favicon path;
- the active project features, as a single row. When ALL_FEATURES is among
  them, the row shows "ALL_FEATURES:" followed by the other active
  features on the same line. The row hides NESTOR (still immature) and the
  deprecated MATHJAX, MERMAID, and SOURCE_FILE_LANGUAGE_PARSERS features,
  since these are enabled by default and no longer need to be listed.

The "Project tree configuration" block (input paths, included/excluded
document and source paths, source root path) moves here from the project
index page, since it is project configuration, not part of the document
tree view. Each input path and the source root path show a truncated
external prefix that a user can click to reveal in full
(path_reveal.js) - useful when the project lives several directories deep
and the full path would otherwise crowd out the parts that matter.

Implementation:
- ProjectConfigurationHTMLGenerator / ProjectConfigurationViewObject
  (strictdoc/features/project_configuration/) render the page; both the
  server router and the static HTML export call the same generator, so
  there is exactly one template to keep in sync.
- ProjectConfig gains a config_path attribute, set by
  ProjectConfigLoader.load_using_server_config() via a new
  _resolve_config_path() helper, so the page can show the exact file that
  was loaded (this required load_from_path_or_get_default() to start
  returning the resolved path alongside the ProjectConfig; every other
  caller of that method just ignores the second value).
- table_key_value/index.jinja gains an "Append" row kind, used to render
  the lazy-loading-threshold default hint on its own line under the value.

Tests: unit coverage for the config_path resolution helpers, and an
end-to-end test covering the page itself plus the external-prefix
reveal/hide toggle for input paths and the source root path.
@mettta
mettta force-pushed the mettta/feat_configuration_page branch 2 times, most recently from 5f76ef9 to bd5c571 Compare September 17, 2026 10:34
@mettta mettta changed the title feat(project-configuration): add project configuration page feat(project-configuration): add read-only Project configuration page Sep 17, 2026
@mettta
mettta force-pushed the mettta/feat_configuration_page branch from bd5c571 to 733ff03 Compare September 17, 2026 11:10
@mettta
mettta requested a review from stanislaw September 17, 2026 11:14
@mettta
mettta marked this pull request as ready for review September 17, 2026 11:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants