Use this file as the primary instruction set for AI agents working in this repository.
Derive new code from existing repository patterns first, especially from libs/f-flow/src.
Treat examples, docs, and the portal app as confirmation of public usage patterns, not as a reason to invent new framework layers.
- Keep the library boundary intact:
@foblex/flowowns interaction, rendering, and internal runtime UI state; application code owns graph data, validation, persistence, and business meaning. - Never invent selectors, inputs, outputs, methods, or React Flow style APIs such as
[nodes],[edges],setNodes(), oraddEdge(). - Preserve established public
f*API names and aliases. When an event exposes both current and deprecated names, prefer the current non-deprecated fields in new consumer code. - Extend existing feature slices before introducing new cross-cutting abstractions.
- Keep public API compatibility in mind. If a change affects exported behavior, update docs/examples/changelog/migration notes together with the code.
libs/f-flow/src/is the primary source of truth for library architecture and naming.- Top-level library slices are feature-oriented and colocated:
f-flow,f-canvas,f-node,f-connectors,f-connection,f-connection-v2,f-draggable,f-storage,f-cache,f-minimap,f-zoom,f-selection-area,f-external-item,f-magnetic-lines,f-magnetic-rects,f-virtual,f-auto-pan,f-backgroud,domain,drag-toolkit,reactivity,mixins,testing,utils. domain/contains internal mediator-driven operations and feature execution classes.f-storage/,f-cache/, andreactivity/are internal runtime support layers, not app-facing graph state layers.libs/f-examplesandlibs/f-pro-examplesare consumer-style examples of the library.apps/f-flow-portal/src/appandapps/f-flow-portal/public/markdownpower the documentation portal and are the source of truth for docs/example registration.- Tests are colocated with source as
*.spec.ts. Test-only helpers live underlibs/f-flow/src/testing.
- Use lowercase kebab-case for folders and file names.
- In
libs/f-flow/src, feature folders usually expose a localindex.ts; keep exports flowing through that barrel. - Many feature folders also expose
providers.ts; use that file for feature provider arrays instead of scattering provider registration across unrelated files. - Requests are named explicitly with a
-request.tssuffix. - Events and event payload helpers use
*-event.tsand*-event-data.ts. - Base abstractions use
*-base.ts. - Angular declarable file naming is mixed:
- older slices still use
*.component.ts,*.directive.ts, and*.service.ts - newer slices often use plain feature names such as
f-auto-pan.ts,f-selection-area.ts,f-connection-path.ts,f-virtual-for.ts
- older slices still use
- Do not append
.component,.directive, or.serviceto new file names by default. Match the local slice, and prefer plain feature names in newer-style folders. - Preserve existing path names exactly. Do not opportunistically rename legacy paths such as
f-backgroud.
- Public exports go through feature-level
index.tsfiles and thenlibs/f-flow/src/public-api.ts. - If a public declarable is also exposed through module compatibility, wire it through the relevant
F_*_PROVIDERSarray andFFlowModule. - Public selectors and aliases keep the repository’s
f*naming scheme:- element selectors such as
f-flow,f-canvas,f-connection - attribute selectors such as
[fNode],[fGroup],[fNodeInput],[fNodeOutput],[fNodeOutlet],[fDraggable]
- element selectors such as
- In library source, use local imports and feature barrels. Do not self-import
@foblex/flowfrom production library code. Self-imports are used in specs to validate the public surface. - Do not treat
[fNodes]or[fConnections]as graph-state inputs. In this repository they are content-projection markers in specific examples.
- Prefer colocating interfaces, types, requests, handlers, events, and helper utilities inside the same feature slice they belong to.
- Keep mediator requests and their execution classes close together in the same feature subtree.
- Prefer feature folders built from granular operations, not from one large facade class.
- The common small-feature shape in
domain/*is:some-feature/some-feature/index.tssome-feature/some-feature.tssome-feature/some-feature-request.ts
- Complex interaction features are split one level further into distinct phases such as
*-preparation,*-handler, and*-finalizeinstead of collapsing the whole flow into one file. - Register execution classes through feature provider arrays such as
F_NODE_FEATURES,F_FLOW_FEATURES, andCOMMON_PROVIDERS. - Keep runtime registries in
f-storage-style support layers instead of building a separate app-like store abstraction inside the library. - Keep specs next to the code they verify. Keep unsafe testing escape hatches inside
libs/f-flow/src/testing/internal, not inside specs.
- Match file names to the exported symbol in kebab-case.
- Name request files after the action plus
-request:add-node-to-store-request.ts. - Name execution/feature files after the action itself, not
*-execution.ts:add-node-to-store.ts,fit-to-flow.ts,reset-scale.ts. - Name base abstractions with a
-base.tssuffix. - Build new domain features as granular folders. Prefer
fit-to-flow/fit-to-flow.tsover adding another unrelated operation into an existing large file. - Do not add
-handlerjust to distinguish a feature implementation from its request. In active domain/features code, the distinction is usually:fit-to-flow.tsfor the execution/featurefit-to-flow-request.tsfor the request
- Reserve
*-handler.tsfor subtrees that already use handler terminology, especially inside drag interaction internals. - In library source, newer Angular declarables often use plain feature names without
.componentor.directive. In examples and portal code, match the local folder pattern before choosing betweenfoo.tsandfoo.component.ts.
- Public library classes use the
Fprefix:FFlowComponent,FNodeDirective,FExternalItemService,FCreateConnectionEvent. - Base abstractions use the
*Basesuffix, not aBase*prefix. - Request classes use the
*Requestsuffix. - Keep execution classes named after the operation they perform, for example
AddNodeToStore,GetNormalizedPoint,SortItemLayers. - In newer Angular declarables, prefer plain feature names such as
FAutoPan,FSelectionArea,FConnectionPath,FVirtualFor. - Keep
Component,Directive, andServicesuffixes only when continuing an older local slice that already uses them. - Keep
*Handlernames only in areas that already model behavior as handlers, mostly drag/interactions internals.
- In library source, interfaces are usually prefixed with
I:IRect,ICurrentSelection,IHasHostElement. - Prefer
interfacefor reusable object contracts and implemented public contracts. - Use
typefor unions, event helper shapes, function-like aliases, or compact local structures. - Enums are usually prefixed with
EF. Preserve existing exceptions rather than normalizing unrelated code. - Enum members must stay
UPPER_CASEto satisfy lint rules.
- Public Angular entities use the
Fprefix andf*selectors/aliases. - Keep
exportAsnames aligned with the public API, for examplefComponent,fDraggable,fNodeOutput. - Host metadata usually adds
f-componentplus a feature-specific class such asf-flow,f-node,f-node-output. - Keep disabled and state modifier classes in the
f-*namespace.
- Event classes use
F*Eventor*EventData. - Preserve compatibility aliases in public event classes when the surrounding event already carries both current and deprecated names.
- Request classes usually declare
static readonly fToken = Symbol('...'). Follow that pattern for mediator requests. - Execution classes are usually distinguished from requests by file/class naming:
some-feature.ts/SomeFeaturesome-feature-request.ts/SomeFeatureRequest
- Do not introduce
*Handlernames where a plain feature/execution name already fits the surrounding folder. - Service names use
F*Servicewhen the class is a real service and the local slice already uses service terminology. - Directive/class suffixes are not a repository-wide rule for new code. Preserve them only where the local slice already uses them.
- Private members must use a leading underscore. This is lint-enforced.
- Prefer
private readonly _dependency = inject(...)for injected dependencies in library code. - Use
readonlyfor event payload fields, injected dependencies, tokens, provider arrays, and other immutable references. - Module-level constants use
UPPER_SNAKE_CASE. Feature/provider constants often use anF_*prefix. - Preserve established public
f*property names and aliases on Angular APIs such asfNodeId,fNodePosition,fSelectionChange,fCreateConnection. - Keep helper methods private/protected and underscore-prefixed when they are internal implementation details.
- In mediator execution classes, the public entry point is
handle(...). - Name execution classes and request classes from the same domain verb:
FitToFlow+FitToFlowRequestResetScale+ResetScaleRequestSelectionAreaPreparation+SelectionAreaPreparationRequest
- Prefer domain verbs in method names over generic names.
- Public component/directive methods are short and purpose-specific, for example:
redrawresetselectAllclearSelectiongetStategetSelectiongetPositionInFlow
- Private helper methods are usually underscore-prefixed and verb-based:
_listen*_emit*_create*_setup*_resolve*_calculate*_store*_find*_build*_update*_remove*
- Predicate helpers typically use
_is*,_can*, or_should*. - Private computed accessors may use underscore-prefixed getters such as
_transformor_instance.
- Mediator-driven request/execution flow via
FMediator.execute(new SomeRequest(...)). - Small feature-local execution classes registered with
@FExecutionRegister(...). - Base-class layering for Angular host primitives such as flow, canvas, node, connector, connection, and drag infrastructure.
- Composition through registries, plugin instances, handlers, and feature provider arrays.
- Event-driven integration where the app handles final events and updates its own state.
- Internal reactivity built on Angular signals plus
FChannel/FChannelHubin library code.
- From Angular components/directives, trigger behavior through mediator requests instead of mutating shared runtime state directly.
- Keep each request/execution pair focused on one operation.
- Keep features granular. If a behavior has distinct stages, split them into separate operations such as preparation, handler, finalize, calculation, registration, or notification.
- Extend an existing feature slice when the new behavior naturally belongs there.
- Use guard clauses and early returns. This is common in the codebase and keeps interaction code readable.
- Keep comments for public API contracts or non-obvious behavior. Do not narrate obvious implementation steps.
- Extract meaningful constants when a number or string has feature-level semantics, for example debounce values, thresholds, registry keys, and CSS class maps.
- Do not introduce an application-owned graph store into
libs/f-flow/src. - Do not add React Flow style data APIs or node/edge store helpers to the library.
- Do not introduce Angular CDK drag-drop into the core interaction pipeline.
- Do not introduce RxJS-based internal state flow into library features that currently use signals and
libs/f-flow/src/reactivity. - Do not add DTO-style indirection or extra architectural layers when a feature already uses colocated requests, events, handlers, and types.
- Do not introduce
*-handlernaming just to mirror a request pair when the surrounding feature uses plain operation names. - Do not remove deprecated compatibility aliases from public events unless the task explicitly includes a breaking API migration.
- UI declarables depend on base classes, mediator requests, and internal support services.
- Execution/handler classes depend on stores, registries, cache, platform abstractions, and utilities, not on application business state.
- Library internals use local imports and feature barrels. Examples and portal code consume the package through
@foblex/flow. - Browser access in library code goes through injected abstractions such as
BrowserService,DOCUMENT, or platform flags.
- Feature-level provider arrays such as
F_*_FEATURESandF_*_PROVIDERS. - Local feature barrels (
index.ts) and the rootpublic-api.ts. - Module compatibility via
FFlowModule. - Mediator request registration via
@FExecutionRegister. - Plugin/instance registration in
FComponentsStore.instancesand related request flows. - Testing helpers exported from
libs/f-flow/src/testing.
- Use
ChangeDetectionStrategy.OnPush. This is the consistent change-detection mode across inspected components. - Keep external templates and styles unless the surrounding file already uses inline metadata.
- Use
host: { ... }metadata for host attrs/classes instead of@HostBindingand@HostListener. - In library code, prefer
inject()-based field injection when extending existing library patterns. - If a public component/directive is part of module compatibility, keep
FFlowModulein sync.
- Treat nodes, groups, connectors, zoom, and draggable behavior as attribute directives when extending similar features.
- Directives commonly extend a base abstraction, register themselves in
ngOnInit, and unregister inngOnDestroy. - Preserve the existing
f*alias pattern on directive inputs and host attributes. - Keep host classes and
data-f-*attributes aligned with the existing runtime styling and lookup conventions.
- Keep services small and feature-specific.
- Use
@Injectable()services for runtime registries, helpers, or support objects, not as a replacement for the app-owned domain model. - Prefer existing stores/registries/channels before inventing a new singleton service.
- Preserve aliased public names exactly, for example
alias: 'fNodeId',alias: 'fNodePosition',alias: 'fNodeSizeChange'. - In library source,
input(),model(), andoutput()are active patterns.@Input()/@Output()/EventEmitterare also still in use. Match the local feature instead of forcing one style. - Prefer current non-deprecated event payload fields in new consumer code:
sourceId/targetId/dropPositionconnectionId/endpointnodes,nodeIds,groupIds,connectionIds
- Design consumer integrations around final-result outputs such as
fMoveNodes,fCreateConnection,fReassignConnection,fDropToGroup,fConnectionWaypointsChanged, andfDragEnded.
- Prefer
templateUrlplusstyleUrlswhen creating new files unless the surrounding area already uses a different metadata style. - Use Angular built-in control flow syntax. The repository uses
@if,@for, and@switch; it does not use*ngIf,*ngFor, or*ngSwitch. - Prefer styling your own semantic classes first, then combine them with verified high-level Foblex classes such as
.f-node,.f-group,.f-connection,.f-selected,.f-dragging, and connector state classes. - Do not depend on deep internal DOM structure for styling unless you inspect the actual rendered DOM in the target version.
- No stable forms/CVA convention is established in the inspected flow library code. Do not invent one unless the local feature already uses it.
- In
libs/f-flow/src, prefer signals and the localreactivityhelpers over RxJS. - Guard browser-only behavior through
BrowserService, injectedDOCUMENT, or explicit browser/platform checks. - Preserve hydration and SSR guards in portal code such as
ngSkipHydrationandIS_BROWSER_PLATFORMchecks when touching SSR-sensitive areas.
- Prefer
interfacefor reusable object shapes and public contracts. - Prefer
typefor unions, literal combinations, compact helper records, and function signatures. - Follow the naming convention already used in the local slice instead of normalizing all types to one prefix.
- Use inheritance when extending an existing
*Baseabstraction or mixin that already defines the feature contract. - Prefer composition for new behavior orchestration: mediator requests, handlers, registries, plugin instances, and utilities.
- Keep execution classes small and single-purpose.
- Use
overridewhenever you override a base member. The compiler enforces this. - Keep at most two classes per file. This is lint-enforced.
- Use generics where the repository already does: registries, channels, builders, typed keys, and helper abstractions.
- Avoid generic-heavy meta-abstractions that have no close precedent in the local slice.
- Use explicit
public,protected, andprivate. - Keep internal helper methods
privateorprotectedinstead of leaving them implicit. - Keep private members underscore-prefixed.
- Mark injected dependencies and immutable references as
readonly. - Keep request payloads and public event payloads immutable where possible.
- Use mutable fields or signals only for real runtime state such as counters, pending flags, geometry, selection, or drag state.
- Do continue the local feature pattern before applying a broader repository pattern.
- Do route new library-side operations through mediator requests when the surrounding feature already uses mediator executions.
- Do colocate new requests, handlers, events, and helper types inside the feature they serve.
- Do split a large behavior into multiple granular feature folders when the local area already models the workflow in steps.
- Do name methods after the domain action they perform, not after generic control flow.
- Do update feature barrels,
public-api.ts, andFFlowModulewhen a public surface actually changes. - Do add colocated
*.spec.tstests for new logic when the surrounding feature already has tests. - Do use the testing kit from
libs/f-flow/src/testinginstead of ad hoc test infrastructure. - Do keep public docs/examples in sync when changing public behavior.
- Do let Prettier and ESLint conventions stand: 2-space indentation, single quotes, semicolons, multiline trailing commas, blank line before
returnwhen required by lint.
- Don’t rename established public API to match generic Angular or React naming preferences.
- Don’t move interfaces or helper types into a separate global folder when the surrounding feature colocates them.
- Don’t invent new state-management layers inside the library.
- Don’t accumulate multiple unrelated operations in one large feature file when the surrounding slice breaks them into separate folders.
- Don’t use
*Handlernaming just to mark “the class that does the work” if the local slice already uses plain feature names plus*Request. - Don’t introduce generic method names such as
process,executeFeature, orrunLogicwhen the surrounding code already has a clearer domain verb. - Don’t bypass browser/platform abstractions with direct
windowordocumentaccess in library code. - Don’t add
console.log; lint only allowsconsole.warnandconsole.error. - Don’t use
as anyin specs. Keep unsafe casts inlibs/f-flow/src/testing/internal. - Don’t “fix” legacy naming or file-path oddities unless the task explicitly requires a migration.
- Start from the nearest local precedent:
- same feature folder first
- same top-level slice second
- another library slice third
- examples/portal last
- When creating a new library file inside an existing feature:
- keep it in the same feature subtree
- use kebab-case naming
- export it through the local
index.ts - if it is a mediator operation, add a
*Requestclass and register the execution in the featureproviders.ts
- When adding a new operation to
domain/*or another granular feature area:- create a dedicated folder for that operation
- add
operation-name.ts - add
operation-name-request.ts - add
index.tsthat re-exports both - register the execution in the parent
providers.ts
- When adding a new public library surface:
- update the feature barrel
- update
libs/f-flow/src/public-api.ts - update
FFlowModuleand the relevantF_*_PROVIDERSarray if the symbol is part of Angular module compatibility - update examples/docs/changelog/migration notes when the change is user-visible
- When continuing an existing Angular feature, preserve the local choice for:
inject()vs constructor injectioninput()/model()/output()vs@Input()/@Output()- plain feature file naming vs
.component.ts/.directive.ts/.service.ts styleUrlsvsstyleUrl
- When creating a new library-side execution, prefer:
some-feature.tsfor the implementationsome-feature-request.tsfor the requestSomeFeatureandSomeFeatureRequestfor class names
- When the behavior has multiple stages, prefer multiple small operations such as
some-feature-preparation,some-feature-finalize,calculate-something,register-something, oremit-somethinginstead of one monolithic class. - Only use
*-handlernaming if the exact subtree already uses handler terminology as part of a larger drag/interaction pipeline. - When adding examples:
- keep them standalone if the surrounding example area is standalone
- import
FFlowModulewhen that is the local example pattern - update the relevant lazy registration config such as
apps/f-flow-portal/src/app/examples.config.ts - add the matching markdown entry under
apps/f-flow-portal/public/markdownwhen the example should appear in docs navigation
- If more than one repository pattern exists, prefer the one already used in the exact folder you are editing.
- If the exact folder is mixed, prefer the dominant pattern inside
libs/f-flow/srcfor library code and the dominant pattern inside the surrounding example/app area for consumer code. - If ambiguity remains after that, preserve existing public API compatibility and choose the option that requires the smallest new abstraction footprint.
The files apps/f-flow-portal/public/llms.txt (short index) and apps/f-flow-portal/public/llms-full.txt (full inline reference) are the AI-consumable documentation served at https://flow.foblex.com/llms.txt and https://flow.foblex.com/llms-full.txt. Context7 and other AI tools index these files. The file context7.json at the repository root configures how Context7 crawls this repository.
- When a public API surface changes: new or removed inputs, outputs, methods, selectors, or types.
- When a new component, directive, or service is added to the public API.
- When a component/directive is deprecated or removed.
- When default values change for existing inputs.
- When new enums, interfaces, or type aliases are added to the public API.
- When breaking changes are introduced in a new major/minor version.
llms.txtis the short index following the llms.txt spec: H1 title, blockquote, H2 sections with- [Link](url): descriptionentries. Keep it as a link directory — no inline API content.llms-full.txtis the full inline reference with all inputs, outputs, methods, types, and code examples. Keep the existing numbered section structure and table format.- Update the version number when bumping the package version.
- Add new components/directives in the API Reference section following the existing table format.
- Update the Angular Version Compatibility table when compatibility changes.
- Keep code examples accurate and using current API names (prefer non-deprecated property names).
- Do not remove documentation for features that still exist.
- Do not add internal/private APIs. Only document what is exported through
public-api.ts.
- When new documentation folders are added that Context7 should crawl.
- When folders should be excluded from crawling.
- When usage rules change for AI consumers.
- The repository is mixed between standalone declarables and
FFlowModulecompatibility. New library code often uses standalone APIs, but module support remains active and heavily used by examples. - Newer library files often favor
inject(),input(),model(), andoutput(), but active code still uses constructor injection and decorator-based inputs/outputs in some features. templateUrl+styleUrlsis dominant, but some portal files usestyleUrl.- Example entry files use both plain names such as
custom-nodes.tsand suffix-based names such asnode-as-connector.component.ts. - Older library slices still contain
.component.ts,.directive.ts,.service.ts, and many*-handler.tsfiles, but newer slices often avoid those suffixes. Keep following the exact local slice when editing mixed areas. - Interfaces strongly favor the
Iprefix, but testing helpers and a few utility contracts include exceptions. Keep following the local slice rather than enforcing the prefix mechanically in unrelated code. - Enums strongly favor the
EFprefix, but a small number of utility enums use descriptive names without it.