HAP Private Deployment Web Edition (pd-openweb)

repository·main·Indexed 42 days ago

https://github.com/mingdaocom/pd-openweb

A frontend-focused architecture for the HAP platform that enables deep customization of visual styles and functional extensions. It includes the ming-ui component library featuring components like RecordCard, Button, Dialog, and Input, as well as specific guidelines for module resolution in HTML templates and version matching requirements for private deployments.

Tokens
61.1K
Snippets
125
Records
190
Agent score
52%

What's inside HAP Openweb

  1. Overview of HAP Private Deployment Web Edition

    main

    HAP Private Deployment Web Edition (pd-openweb) provides an open frontend architecture designed for high personalization. It allows developers to create custom visual effects and implement customized functional features.

    Critical Compatibility Requirements:

    • Version Matching: The source code version used for development must strictly match the version of the deployed HAP instance to avoid API incompatibilities.
    • Upgrades: If you perform secondary development (customization), you must manually merge and synchronize your custom source code with the matching version when upgrading the HAP private edition to maintain system integrity.
  2. Understand Custom Fields (Storage vs. Reference types)

    main

    Custom Fields allow you to use React syntax to customize the style and interaction of a field. There are two distinct types of custom fields:

    1. Storage Type (存储类型): Based on existing controls, these fields can store values. They are used when you want the custom component to manage and save data to the record.
    2. Reference Type (引用类型): These fields are used only for presenting existing data. They do not store values themselves but instead pull data from other fields in the form.

    Key Constraints:

    • Components must be written as a single function.
    • You cannot use import statements; all logic must be implemented natively within the function.
    • All React properties/hooks are pre-exposed and available for use.
    • Tailwind CSS is supported natively.
  3. Important usage notes for Ming-UI

    main

    When using Ming-UI, keep the following best practices and constraints in mind:

    1. Importing Components: All components should be imported directly from ming-ui (e.g., import { Button } from 'ming-ui'). You do not need to specify sub-paths.
    2. Styling: While utility classes are available, prefer using component-specific props for styling when possible.
    3. Internationalization: Use the _l() function to handle text internationalization.
    4. Component Control: Components like Input and Checkbox support both controlled and uncontrolled modes.
    5. Callbacks: Be aware that callback function parameter formats may vary between different components.
  4. How session recovery and plan restoration work

    main

    The Anonymous Funnel implements a server-first approach to session recovery and plan reconstruction:

    Session Recovery Logic

    • History Priority: Instead of relying on local snapshots, the system prioritizes server history.
    • Resuming Sessions: If the endpoint /agent/sessions/{id}/messages is non-empty, the session is automatically resumed.
    • New Sessions: If the history is empty, the system consumes handoff or pending messages to trigger the initial execute/stream call.
    • Continued Chat: To continue a conversation, the client reuses the existing sessionId when calling agent/execute/stream with a new message. The current version baseline is then updated using the new completed artifactVersionId.

    Plan Reconstruction

    When restoring historical data, the system automatically reads assistant artifacts from: /artifacts/{artifactId}/versions/{versionId}/files/{path}. By retrieving the /plan.md and JSON files, the system can reconstruct:

    • The Plan Card
    • AppBuilder
    • Mobile Overview
  5. Understand Portal Account Test Coverage and Limitations

    main

    The regression tests for portalAccount cover critical security boundaries but have specific limitations that developers must account for when modifying request or interaction logic.

    What is covered

    • Info.test.js: Covers state contracts, refusal to edit without credentials, retry/re-validation, duplicate submission protection, failure recovery, request sequencing (A→B→A), and isolation of old responses after switching credentials or unmounting. It also protects against submission attempts during React.lazy loading when form refs are not yet established.
    • restart.test.js: Covers WeChat mobile/PC entry points, PC QR code scanning, standard login, custom path recovery, double-slash fallbacks, and rejection of unauthorized external site/script/old callback redirects. It verifies that successful logins preserve business query/hash while removing only the portal identity callback keys.

    Known Limitations and Constraints

    • No E2E/Network: Tests are not full browser E2E tests. They do not perform real API calls, file uploads, field association network requests, or OAuth flows.
    • WeChat ReturnUrl: Full WeChat mobile/PC authentication round-trips may still lose the ReturnUrl and redirect to the app homepage. Tests only verify clean restarts and re-initiating WeChat authentication.
    • Exactly-once Guarantee: If a request/response is lost, the server may have already consumed the State. Re-submission might trigger re-validation; exactly-once delivery is not guaranteed.
    • Recovery Scope: Failure recovery is limited to the temporary context owned by the current anonymous form. It will not overwrite other credentials or already logged-in accounts.
    • Deployment Requirement: Deployment must be paired with backend GetUserCollect / InfoLogin validation specifically for information collection purposes.
    • State/Cache: Existing State solutions remain unchanged. Cache isolation issues are deferred. If an old State lacks information collection permissions, it requires re-validation.
  6. Configure Mingo Organization Selector

    main

    The Mingo organization selector follows these selection rules:

    • Visibility: Only organizations that do NOT have the !cannotCreateApp flag are displayed.
    • Single Organization: If the user belongs to only one organization, the selector is bypassed, and the app is created/claimed directly using that projectId.
    • Multi-Organization: Users can select an organization by clicking its row in the selector.
    • No Permissions: If a user attempts to access an organization without permissions, a fallback prompt is shown to initiate a fresh setup.
  7. How the Anonymous Funnel works

    main

    The Anonymous Funnel allows visitors to interact with the AI agent to generate an application 'plan' before being required to log in.

    The Workflow:

    1. Anonymous Input: A visitor enters requirements on the marketing site. This creates a new sessionId.
    2. Plan Generation: The user is redirected to the HAP plan page (/public/mingo/plan). The agent uses the app-plan-builder-public agent name to stream a plan.
    3. Handoff: The session is stored in webCache using a handoff key.
    4. Conversion (Login): When the user clicks "Generate Application" (生成应用), they are prompted to log in.
    5. Claiming the Session: After login, the user selects an organization. The system calls claimAnonymousSession({sessionId, projectId}) to link the anonymous plan to their logged-in account, allowing them to continue building in the full product version.
  8. Understand key states in Tab navigation

    main

    The Tab navigation system relies on several key state variables to manage focus and visual feedback:

    • tabFocusArr: An array tracking focus history.
      • tabFocusArr[0]: The ID of the currently active control.
      • tabFocusArr[1]: The ID of the previously active control.
    • tabFocusActive: A boolean calculated in DeskFormWidget.jsx. It is true if the current controlId is included in the tabFocusId and the control is not disabled. This is used to:
      • Pass focus status to child components.
      • Apply the .customFormItemTabFocus CSS class for visual feedback.

    Visual Feedback Styling: Use the following selector in your LESS/CSS to style the focused state: .customFormItemControl.customFormItemTabFocus

  9. How Tab key navigation works in DesktopForm

    main

    DesktopForm implements a keyboard-friendly navigation system using a publish-subscribe event management system. This allows users to quickly switch between form controls using the Tab key.

    Core Architecture

    1. WidgetEventManager: A central manager that handles subscriptions (subscribe) and event broadcasting (publish) based on controlId. It also supports cleaning up subscriptions via clear(instanceId).
    2. Hooks System:
      • useFormEventManager: Manages form-level events, global keyboard listening, and the logic for calculating the next Tab target.
      • useWidgetEvent: A widget-level hook used by individual controls to subscribe to and respond to Tab enter/leave events.

    Event Types

    Event TypeTrigger TimingPurpose
    trigger_tab_enterWhen Tab switches to this controlControl gains focus and enters edit mode
    trigger_tab_leaveWhen Tab leaves this controlControl loses focus and exits edit mode
    EnterWhen Enter key is pressedPassed to the currently active control
    ArrowRight / ArrowLeftWhen arrow keys are pressedPassed to the currently active control

    Workflow

    1. User presses Tab.
    2. handleTabChange captures the event and calls event.preventDefault() to stop default browser behavior.
    3. The system queries all .customFormItem elements to build a list of controls.
    4. supportTabKeyDown() determines the next valid control.
    5. A trigger_tab_leave event is published to the current control.
    6. A trigger_tab_enter event is published to the target control.
    7. The tabFocusArr state is updated to track the current and previous control IDs.
    8. The target control receives the event and executes its focus logic.
  10. Implement a Storage Type Custom Field

    main

    A Storage Type component must accept value and onChange props.

    • value: The current value of the field.
    • onChange: A function to call with the new value to update the field.

    Icons: Lucide icons are pre-installed. To use them, use the LucideIcon component. The name prop must be the icon name in PascalCase (e.g., RefreshCcw). Other props follow the standard lucide-react API.

    function RandomIdGenerator({ value, onChange }) {
      const [id, setId] = useState(value);
    
      return (
        <div className="flex items-center">
          <span className="flex-1 text-[#151515] text-[20px] font-bold text-center">{id}</span>
          <button
            onClick={() => {
              const newId = Math.random().toString(36).substr(2, 10);
              setId(newId);
              onChange(newId);
            }}
            className="p-2 bg-[#1677ff] text-white rounded-full"
          >
            <LucideIcon name="RefreshCcw" size={16} />
          </button>
        </div>
      );
    }
  11. Embed the Mingo Entry Widget on a Next.js website

    main

    To integrate the AI application builder into a marketing website (e.g., a Next.js static site) without adding React or ming-ui dependencies, use the standalone JS bundle. The widget provides an input box and example chips for anonymous user interaction.

    1. Load the script: Include the generated bundle via a <script> tag.
    2. Mount the widget: Call window.MingoEntry.mount() on a target container element.

    Configuration Options for mount:

    • webUrl: The base URL of the HAP application.
    • apiServer: The API server endpoint.
    • agentUrl: The agent service endpoint.

    Behavior based on login status:

    • Logged-in users: The widget redirects to the product version (e.g., /mingo/chat/:sessionId?entry=1 on PC).
    • Anonymous users: The widget triggers an anonymous session and redirects to the HAP plan page (/public/mingo/plan).
    <!-- 1. Load the bundle -->
    <script src="/dist/mingo-entry-widget/mingo-entry-widget.js"></script>
    
    <!-- 2. Target container -->
    <div id="mingo-widget-container"></div>
    
    <script>
      // 3. Mount the widget
      window.MingoEntry.mount(document.getElementById('mingo-widget-container'), {
        webUrl: 'https://your-hap-domain.com',
        apiServer: 'https://your-api-domain.com',
        agentUrl: 'https://your-agent-domain.com'
      });
    </script>
  12. Implement a Reference Type Custom Field

    main

    A Reference Type component does not use value or onChange. Instead, it retrieves data from the formData object using identifiers provided in the env object.

    Workflow:

    1. In the editor, add the target field to the 'References' (引用) section and set an alias (别名).
    2. Access the value in your component using formData[env.alias]?.value.

    Example: Displaying a value from a field aliased as name in red text.

    function NameDisplay({ formData, env }) {
      const targetControlId = env.name; // 'name' is the alias set in the editor
      const targetValue = formData[targetControlId]?.value || ''; 
    
      return <div className="text-2xl text-red-500">{targetValue || '名称未设置'}</div>;
    }