create-lupine 1.0.0 → 1.0.2

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.
package/index.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  import fs from 'node:fs';
4
4
  import path from 'node:path';
@@ -190,6 +190,8 @@ async function init() {
190
190
  const templateObj = TEMPLATES.find((t) => t.name === template);
191
191
  if (templateObj && templateObj.needsPress) {
192
192
  pkg.dependencies['lupine.press'] = '^1.0.1';
193
+ pkg.devDependencies['gray-matter'] = '^4.0.3';
194
+ pkg.devDependencies['marked'] = '^17.0.1';
193
195
  }
194
196
 
195
197
  fs.writeFileSync(path.join(root, 'package.json'), JSON.stringify(pkg, null, 2));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-lupine",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Scaffolding tool for Lupine.js projects",
5
5
  "bin": {
6
6
  "create-lupine": "index.js"
@@ -0,0 +1,210 @@
1
+ # AI Context for Lupine.js
2
+
3
+ **SYSTEM ROLE**: You are an expert developer in `lupine.js`, a custom TypeScript full-stack framework.
4
+
5
+ **🛑 CRITICAL WARNINGS 🛑**
6
+
7
+ 1. **NO REACT HOOKS**: `useState`, `useEffect`, `useReducer`, `useCallback`, `useContext` **DO NOT EXIST**.
8
+ 2. **NO VIRTUAL DOM STATE**: Changing a variable DOES NOT re-render the component. You must manually update `HtmlVar.value`.
9
+ 3. **NO CONTROLLED INPUTS**: Do not bind `value={state}`. Read values from DOM on submit.
10
+
11
+ ---
12
+
13
+ ## 1. Core Philosophy & Reactivity
14
+
15
+ - **`HtmlVar` is the "State"**:
16
+ - Use `HtmlVar` to wrap dynamic sections (lists, conditional renderings, async content).
17
+ - **Pattern**: `const dom = new HtmlVar(initialContent);` -> JSX `{dom.node}` -> `dom.value = updatedContent`.
18
+ - **Direct DOM Access**:
19
+ - Use `RefProps` to get reference to the component root.
20
+ - Use `ref.$(selector)` to find elements (inputs, containers).
21
+ - **Value Retrieval**: `const val = ref.$('input.my-class').value`.
22
+
23
+ ## 2. Key Interfaces
24
+
25
+ ### `RefProps` (Lifecycle & DOM)
26
+
27
+ ```typescript
28
+ const ref: RefProps = {
29
+ // Mounted: Initialize data, timers, events
30
+ onLoad: async (el: Element) => {
31
+ await loadData();
32
+ // ref.$('.sub-element').addEventListener(...)
33
+ },
34
+ // Unmounting: Cleanup
35
+ onUnload: async (el: Element) => {
36
+ // Cleanup (timers, sockets)
37
+ }
38
+ };
39
+ // Usage
40
+ <div ref={ref}>...</div>
41
+ ```
42
+
43
+ ### `CssProps` (Styling)
44
+
45
+ Supports nesting and media queries. **Prefer this over inline styles.**
46
+
47
+ ```typescript
48
+ import { MediaQueryRange } from "lupine.components";
49
+
50
+ const css: CssProps = {
51
+ display: "flex",
52
+ ".child": { color: "var(--primary-color)" },
53
+ // Responsive
54
+ [MediaQueryRange.MobileBelow]: {
55
+ flexDirection: "column",
56
+ },
57
+ };
58
+ ```
59
+
60
+ ## 3. Styles & Themes ("The Look")
61
+
62
+ ### Global Variables (Theming)
63
+
64
+ **NEVER hardcode colors** (e.g., `#000`). Always use CSS variables to support Dark/Light modes.
65
+
66
+ - **Colors**: `var(--primary-color)`, `var(--primary-bg-color)`, `var(--secondary-color)`, `var(--error-color)`.
67
+ - **Borders**: `var(--primary-border)`, `var(--border-radius-m)`.
68
+ - **Spacing**: `var(--space-m)` (8px), `var(--space-l)` (16px).
69
+
70
+ ### Standard Utility Classes
71
+
72
+ - **Flexbox**: `.row-box` (flex row, align-center), `.col` (flex: 1).
73
+ - **Margins/Padding**: `m-auto`, `p-m`, `mt-s`, `pb-l` (s=small, m=medium, l=large).
74
+ - **Text**: `.text-center`, `.ellipsis`.
75
+
76
+ ### Standard UI Components
77
+
78
+ #### Settings Group (Mobile/Desktop Settings)
79
+
80
+ ```tsx
81
+ <div class='setting-section-group'>
82
+ <div class='setting-section-title'>Section Title</div>
83
+ <div class='setting-section-block'>
84
+ <div class='setting-section-item' onClick={...}>
85
+ <div class='setting-section-item-text'>My Option</div>
86
+ <div class='setting-section-item-icon'><i class='ifc-icon ma-chevron-right'></i></div>
87
+ </div>
88
+ </div>
89
+ </div>
90
+ ```
91
+
92
+ #### Admin Edit Row (Form Layout)
93
+
94
+ Standard label + input row pattern for admin pages.
95
+
96
+ ```tsx
97
+ <div class="row-box mt-m">
98
+ <div class="label-class">Title: </div>
99
+ <div class="flex-1">
100
+ {/* width-100p ensures input fills the flex-1 container */}
101
+ <input
102
+ type="text"
103
+ class="input-base my-input-class w-100p"
104
+ value={item.title}
105
+ />
106
+ </div>
107
+ </div>
108
+ ```
109
+
110
+ ## 4. Common Patterns ("The Lupine Way")
111
+
112
+ ### List / Search (No Re-render)
113
+
114
+ **Pattern**: Create a render function (`makeList`) and assign its result to `HtmlVar`.
115
+
116
+ ```typescript
117
+ const MyPage = () => {
118
+ // 1. Logic Variables (Not State)
119
+ let pageIndex = 0;
120
+
121
+ // 2. Dynamic Container
122
+ const listDom = new HtmlVar(<div>Loading...</div>);
123
+
124
+ // 3. Render Function
125
+ const makeList = async () => {
126
+ const data = await fetchData(pageIndex);
127
+ return <div>{data.map(item => <Item item={item} />)}</div>;
128
+ };
129
+
130
+ // 4. Events
131
+ const onSearch = async () => {
132
+ // Read directly from DOM
133
+ const query = ref.$('input.search').value;
134
+ // Update logic var
135
+ pageIndex = 0;
136
+ // Update UI manually
137
+ listDom.value = await makeList();
138
+ };
139
+
140
+ const ref: RefProps = {
141
+ onLoad: async () => {
142
+ listDom.value = await makeList();
143
+ }
144
+ };
145
+
146
+ return (
147
+ <div ref={ref}>
148
+ <input class="search" />
149
+ <button onClick={onSearch}>Go</button>
150
+ {/* Embed Dynamic Content */}
151
+ {listDom.node}
152
+ </div>
153
+ );
154
+ };
155
+ ```
156
+
157
+ ### Mobile Navigation (`SliderFrame`)
158
+
159
+ Lupine uses a "Slide-over" model for navigation (Drill-down).
160
+
161
+ ```typescript
162
+ import { SliderFrame, SliderFrameHookProps, HeaderWithBackFrame } from 'lupine.components';
163
+
164
+ // Parent Component
165
+ const Parent = () => {
166
+ const sliderHook: SliderFrameHookProps = {};
167
+
168
+ const openDetail = (id) => {
169
+ // Push new view onto stack
170
+ sliderHook.load!(<DetailComponent id={id} sliderFrameHook={sliderHook} />);
171
+ };
172
+
173
+ return (
174
+ <div>
175
+ <SliderFrame hook={sliderHook} />
176
+ <div onClick={() => openDetail(1)}>Click Me</div>
177
+ </div>
178
+ );
179
+ }
180
+
181
+ // Child Component
182
+ const DetailComponent = (props) => {
183
+ return (
184
+ <HeaderWithBackFrame
185
+ title="Detail Page"
186
+ onBack={(e) => props.sliderFrameHook.close!(e)}
187
+ >
188
+ Content...
189
+ </HeaderWithBackFrame>
190
+ );
191
+ }
192
+ ```
193
+
194
+ ## 5. Architecture Cheat Sheet
195
+
196
+ - **`lupine.api` (Backend)**:
197
+ - `req.locals.json()` to get body.
198
+ - `apiCache.getDb().selectObject('$__table', ...)`
199
+ - `ApiHelper.sendJson(req, res, { status: 'ok' })`
200
+ - **`lupine.web` (Frontend)**:
201
+ - `NotificationMessage.sendMessage('Msg', NotificationColor.Success)`
202
+ - `getRenderPageProps().renderPageFunctions.fetchData('/api/...')`
203
+
204
+ ## 6. Coding Standards & Gotchas
205
+
206
+ - **❌ React Hooks**: `useState`, `useEffect` **DO NOT EXIST**. Use `HtmlVar` and `RefProps`.
207
+ - **❌ `className`**: Use standard HTML `class`.
208
+ - **⚠️ `style={{}}`**: **Allowed** for simple or dynamic inline styles (e.g., `style={{ border: '1px solid red' }}`), but **prefer `css={CssProps}`** for structural/theme styling.
209
+ - **✅ Native Events**: `onClick`, `onChange`, `onInput`, `onMouseMove` etc. are standard HTML events and **ARE ALLOWED**. Use them for triggering logic or callbacks (e.g., `onInput={(e) => updateOtherThing(e.target.value)}`).
210
+ - **✅ Uncontrolled Inputs**: While you _can_ use `onInput` to track state, the default efficient pattern is often to read `ref.$('input').value` only when the user clicks "Save" or "Search".
@@ -7,7 +7,6 @@ import {
7
7
  isFrontEnd,
8
8
  debugWatch,
9
9
  webEnv,
10
- setDefaultMetaDescription,
11
10
  bindGlobalStyle,
12
11
  } from 'lupine.components';
13
12
  import { bindPressData, PressPage, pressThemes, setPressSubDir } from 'lupine.press';
@@ -23,7 +22,6 @@ bindLang('en', {});
23
22
  bindTheme('light', pressThemes);
24
23
  bindGlobalStyle('comm-css', baseCss, false, true);
25
24
  setDefaultPageTitle('Doc Starter');
26
- setDefaultMetaDescription('Doc Starter Demo');
27
25
 
28
26
  bindPressData(markdownConfig);
29
27
  setPressSubDir('/github-pj-name');
@@ -7,7 +7,6 @@ import {
7
7
  isFrontEnd,
8
8
  debugWatch,
9
9
  webEnv,
10
- setDefaultMetaDescription,
11
10
  bindGlobalStyle,
12
11
  } from 'lupine.components';
13
12
  import { bindPressData, PressPage, pressThemes } from 'lupine.press';
@@ -22,8 +21,7 @@ if (isFrontEnd() && webEnv(ClientEnvKeys.NODE_ENV, '') === 'development') {
22
21
  bindLang('en', {});
23
22
  bindTheme('light', pressThemes);
24
23
  bindGlobalStyle('comm-css', baseCss, false, true);
25
- setDefaultPageTitle('LupineJS Doc');
26
- setDefaultMetaDescription('LupineJS Doc');
24
+ setDefaultPageTitle('Lupine.js Doc');
27
25
 
28
26
  bindPressData(markdownConfig);
29
27
 
@@ -1,3 +1,7 @@
1
+ // css order is important
2
+ import './styles/global.css';
3
+ import './styles/app.css';
4
+
1
5
  import { bindRouter, CssProps, debugWatch, HtmlVar, isFrontEnd, PageRouter, webEnv } from 'lupine.components';
2
6
 
3
7
  const HelloPage = () => {
File without changes
@@ -0,0 +1 @@
1
+ export const baseCss = {};