iphone-xudale 0.2.9
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/.eslintrc.json +3 -0
- package/app/(pages)/options/page.tsx +720 -0
- package/app/(pages)/popup/page.tsx +166 -0
- package/app/(pages)/tips/page.tsx +40 -0
- package/app/components/DropListBox.tsx +157 -0
- package/app/components/SVGPlay.tsx +14 -0
- package/app/favicon.ico +0 -0
- package/app/globals.css +27 -0
- package/app/layout.tsx +15 -0
- package/app/page.tsx +3 -0
- package/app/scripts/content/checkoutSteps.ts +341 -0
- package/app/scripts/content/doFroApplePages.ts +206 -0
- package/app/scripts/content/getPageInitInfo.ts +49 -0
- package/app/scripts/content/getStoreCanPickInfo.ts +251 -0
- package/app/scripts/content/goOrderSteps.ts +179 -0
- package/app/scripts/content/index.ts +56 -0
- package/app/scripts/content/playSystemNotifacation.ts +39 -0
- package/app/scripts/content/sendSelfNotificatioin.ts +27 -0
- package/app/scripts/inject/index.ts +18 -0
- package/app/shared/constants.ts +236 -0
- package/app/shared/interface.ts +25 -0
- package/app/shared/location/city.json +1774 -0
- package/app/shared/location/county.json +17115 -0
- package/app/shared/location/province.json +94 -0
- package/app/shared/util.ts +93 -0
- package/buildAfter.js +86 -0
- package/bunBuild.ts +54 -0
- package/extension/content-script.js +7 -0
- package/extension/favicon.ico +0 -0
- package/extension/icons/icon128.png +0 -0
- package/extension/icons/icon16.png +0 -0
- package/extension/icons/icon19-disable.png +0 -0
- package/extension/icons/icon19.png +0 -0
- package/extension/icons/icon32.png +0 -0
- package/extension/icons/icon38.png +0 -0
- package/extension/icons/icon48.png +0 -0
- package/extension/inject-script.js +1 -0
- package/extension/manifest.json +38 -0
- package/extension/service-worker.js +62 -0
- package/extension.next.config.js +25 -0
- package/icon_generator.py +30 -0
- package/middleware.ts +21 -0
- package/next.config.js +15 -0
- package/package.json +67 -0
- package/postcss.config.js +6 -0
- package/public/assets/images/SCR-20230916-nbkz.png +0 -0
- package/public/assets/images/SCR-20230916-nbyv.png +0 -0
- package/public/assets/images/SCR-20230916-ncte.png +0 -0
- package/public/assets/images/SCR-20230916-ndgw.png +0 -0
- package/public/assets/images/SCR-20230916-ndks.png +0 -0
- package/public/assets/images/SCR-20230916-neaa.png +0 -0
- package/public/assets/images/SCR-20230916-neeq.jpeg +0 -0
- package/public/assets/images/SCR-20230916-nfkt.png +0 -0
- package/public/assets/images/SCR-20230919-ulfn.png +0 -0
- package/public/assets/images/SCR-20230919-ulzd.png +0 -0
- package/public/assets/images/SCR-20230919-uocr.png +0 -0
- package/public/icon_original.png +0 -0
- package/public/next.svg +1 -0
- package/public/vercel.svg +1 -0
- package/tailwind.config.ts +20 -0
- package/tsconfig.json +27 -0
- package/types/global.d.ts +19 -0
@@ -0,0 +1,166 @@
|
|
1
|
+
'use client'
|
2
|
+
import { restoreFromStorage, saveToStorage } from '@/app/shared/util'
|
3
|
+
import { defaultiPhoneOrderConfig, storeKeys } from '@/app/shared/constants'
|
4
|
+
import { useEffect, useState } from 'react'
|
5
|
+
import { Match_URL } from '@/app/shared/constants'
|
6
|
+
import { IPHONEORDER_CONFIG } from '@/app/shared/interface'
|
7
|
+
import SVGPlay from '@/app/components/SVGPlay'
|
8
|
+
|
9
|
+
const Popup = () => {
|
10
|
+
const [orderEnabled, setOrderEnable] = useState<boolean>(false)
|
11
|
+
const [config, setConfig] = useState<IPHONEORDER_CONFIG>(defaultiPhoneOrderConfig)
|
12
|
+
// 异步获取enable状态
|
13
|
+
useEffect(() => {
|
14
|
+
const getOrderEnable = async () => {
|
15
|
+
const isEnabled = await restoreFromStorage(storeKeys.orderEnabled)
|
16
|
+
const config = await restoreFromStorage(storeKeys.orderConfig)
|
17
|
+
setOrderEnable(!!isEnabled)
|
18
|
+
setConfig(config as IPHONEORDER_CONFIG)
|
19
|
+
}
|
20
|
+
getOrderEnable()
|
21
|
+
}, [])
|
22
|
+
|
23
|
+
const handleOptionClick = () => {
|
24
|
+
if (typeof chrome !== 'undefined' && chrome?.runtime) {
|
25
|
+
chrome.runtime.openOptionsPage()
|
26
|
+
} else {
|
27
|
+
console.log(`please open in chrome`)
|
28
|
+
}
|
29
|
+
}
|
30
|
+
const handleConfirm = () => {
|
31
|
+
if (
|
32
|
+
!orderEnabled ||
|
33
|
+
(config?.lastName &&
|
34
|
+
config?.mobile &&
|
35
|
+
config?.firstName &&
|
36
|
+
config?.appleId &&
|
37
|
+
config?.last4code &&
|
38
|
+
config?.cityName &&
|
39
|
+
config?.districtName &&
|
40
|
+
config?.provinceName)
|
41
|
+
) {
|
42
|
+
confirmAsync(orderEnabled)
|
43
|
+
} else {
|
44
|
+
setOrderEnable(false)
|
45
|
+
alert(`请先配置必要信息`)
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
const handleTestNotification = () => {
|
50
|
+
if (typeof chrome !== 'undefined' && chrome?.tabs) {
|
51
|
+
// @ts-ignore
|
52
|
+
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
|
53
|
+
chrome.tabs.sendMessage(tabs[0].id, {
|
54
|
+
data: 'playSystemNotification',
|
55
|
+
voiceInfo: config?.voiceInfo || {},
|
56
|
+
})
|
57
|
+
})
|
58
|
+
chrome.runtime.sendMessage()
|
59
|
+
}
|
60
|
+
|
61
|
+
console.log(`chrome?.runtime`, chrome?.tabs)
|
62
|
+
}
|
63
|
+
|
64
|
+
return (
|
65
|
+
<div className="mx-auto mt-1 mb-2 w-[18rem] h-[15rem]">
|
66
|
+
<main className="flex w-fit flex-col items-center gap-2 justify-between py-3 px-2 mx-auto mb-2 mt-2">
|
67
|
+
<div className="flex w-full gap-3 justify-center py-1 mb-2 bg-white border-b border-solid border-slate-300">
|
68
|
+
<div
|
69
|
+
className={`flex w-40 h-9 ${'bg-indigo-600 cursor-pointer'} bg-opacity-90 border border-indigo-500 rounded-md my-2 items-center align-middle justify-center text-center min-w-min px-3 hover:shadow-md hover:bg-indigo-500`}
|
70
|
+
onClick={handleTestNotification}
|
71
|
+
>
|
72
|
+
<SVGPlay className="w-5 h-5 mr-2" />
|
73
|
+
通知测试
|
74
|
+
</div>
|
75
|
+
</div>
|
76
|
+
<div className="flex flex-row gap-3 h-10 justify-between mb-2 px-4 py-6 rounded-xl bg-slate-100">
|
77
|
+
<div className="flex text-gray-600 items-center text-base font-bold">开启自动抢购</div>
|
78
|
+
<SelectItem
|
79
|
+
enabled={orderEnabled}
|
80
|
+
index={0}
|
81
|
+
callback={({ enabled }) => {
|
82
|
+
setOrderEnable(enabled)
|
83
|
+
}}
|
84
|
+
/>
|
85
|
+
</div>
|
86
|
+
</main>
|
87
|
+
<div className="w-full flex flex-row justify-center text-center items-center gap-5 text-sm">
|
88
|
+
<div
|
89
|
+
className={`flex w-1/3 h-9 bg-white text-indigo-500 cursor-pointer bg-opacity-90 border-4 border-t-2 border-indigo-500 rounded-3xl my-2 items-center align-middle justify-center text-center min-w-min px-3 hover:shadow-md hover:border-t-[3px] hover:border-b-[3px]`}
|
90
|
+
onClick={handleOptionClick}
|
91
|
+
>
|
92
|
+
配置
|
93
|
+
</div>
|
94
|
+
<div
|
95
|
+
className={`flex w-1/3 h-9 ${'bg-indigo-600 cursor-pointer'} bg-opacity-90 border border-indigo-500 rounded-3xl my-2 items-center align-middle justify-center text-center min-w-min px-3 hover:shadow-md hover:bg-indigo-500`}
|
96
|
+
onClick={handleConfirm}
|
97
|
+
>
|
98
|
+
确认
|
99
|
+
</div>
|
100
|
+
</div>
|
101
|
+
</div>
|
102
|
+
)
|
103
|
+
}
|
104
|
+
|
105
|
+
export default Popup
|
106
|
+
|
107
|
+
interface ISelectItemProps {
|
108
|
+
enabled?: boolean
|
109
|
+
index: number
|
110
|
+
callback: ({ enabled }: { enabled: boolean }) => void
|
111
|
+
}
|
112
|
+
const SelectItem = ({ enabled, callback }: ISelectItemProps) => {
|
113
|
+
const handleToggle = () => {
|
114
|
+
callback({
|
115
|
+
enabled: !enabled,
|
116
|
+
})
|
117
|
+
}
|
118
|
+
return (
|
119
|
+
<div className="flex flex-row gap-6">
|
120
|
+
<div className="w-20 flex-col justify-center items-end gap-1.5 inline-flex">
|
121
|
+
{enabled ? (
|
122
|
+
<div
|
123
|
+
className="w-14 h-7 bg-indigo-600 bg-opacity-70 rounded-2xl py-0.5 px-[0.2rem] flex relative cursor-pointer ease-linear duration-500 shadow-indigo-500/50 shadow-md"
|
124
|
+
onClick={handleToggle}
|
125
|
+
>
|
126
|
+
<div className="w-6 h-6 bg-gray-100 rounded-full pt-1 pb-2 px-1 ease-linear duration-300 ml-[1.65rem]"></div>
|
127
|
+
</div>
|
128
|
+
) : (
|
129
|
+
<div
|
130
|
+
className="w-14 h-7 bg-slate-400 bg-opacity-50 rounded-2xl py-0.5 px-[0.2rem] flex relative cursor-pointer ease-linear duration-500 shadow-slate-500/50 shadow-md"
|
131
|
+
onClick={handleToggle}
|
132
|
+
>
|
133
|
+
<div className="w-6 h-6 bg-gray-100 rounded-full pt-1 pb-2 px-1 ease-linear duration-300 ml-0"></div>
|
134
|
+
</div>
|
135
|
+
)}
|
136
|
+
</div>
|
137
|
+
</div>
|
138
|
+
)
|
139
|
+
}
|
140
|
+
|
141
|
+
interface ICondirmLoadProps {
|
142
|
+
callback?: (msg: string) => void
|
143
|
+
}
|
144
|
+
const confirmLoad = async ({ callback }: ICondirmLoadProps) => {
|
145
|
+
let msg = ``
|
146
|
+
if (typeof chrome === 'undefined' || !chrome?.tabs) {
|
147
|
+
msg = 'Please use as chrome extension'
|
148
|
+
callback && callback(msg)
|
149
|
+
return
|
150
|
+
}
|
151
|
+
|
152
|
+
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true })
|
153
|
+
const tabUrl = (tab?.url || '').toLowerCase()
|
154
|
+
const urlObj = new URL(tabUrl)
|
155
|
+
const isInMatchUrl = urlObj.href.includes(Match_URL)
|
156
|
+
|
157
|
+
if (isInMatchUrl) {
|
158
|
+
chrome.tabs.reload(tab.id)
|
159
|
+
return
|
160
|
+
}
|
161
|
+
}
|
162
|
+
|
163
|
+
const confirmAsync = async (orderEnabled: boolean) => {
|
164
|
+
await saveToStorage(orderEnabled, storeKeys.orderEnabled)
|
165
|
+
window.close()
|
166
|
+
}
|
@@ -0,0 +1,40 @@
|
|
1
|
+
'use client'
|
2
|
+
import { useEffect, useState } from 'react'
|
3
|
+
import { iframeMessagePass } from '@/app/shared/constants'
|
4
|
+
|
5
|
+
const Tips = () => {
|
6
|
+
const [fetchCount, setFetchCount] = useState(0)
|
7
|
+
const [beforeReload, setBeforeReload] = useState(50)
|
8
|
+
useEffect(() => {
|
9
|
+
window.addEventListener('message', function (event) {
|
10
|
+
if (event.data.action === iframeMessagePass.messageAction) {
|
11
|
+
setFetchCount(event.data.count)
|
12
|
+
setBeforeReload(event.data.beforeReload)
|
13
|
+
}
|
14
|
+
})
|
15
|
+
}, [])
|
16
|
+
|
17
|
+
const bgTransparent = `body{background: transparent}`
|
18
|
+
if (!fetchCount)
|
19
|
+
return (
|
20
|
+
<div>
|
21
|
+
<style>{bgTransparent}</style>
|
22
|
+
</div>
|
23
|
+
)
|
24
|
+
return (
|
25
|
+
<main className="flex w-52 h-fit flex-col text-gray-700 items-center gap-1 px-2 justify-between py-2 mx-auto rounded-lg bg-slate-100 text-base font-bold cursor-pointer shadow-slate-500/50 shadow-lg">
|
26
|
+
<style>{bgTransparent}</style>
|
27
|
+
<div className="flex flex-row gap-0 h-8 items-center justify-center py-2 ">
|
28
|
+
<div className=" ">
|
29
|
+
当前正在重试:
|
30
|
+
<span className=" text-indigo-700">{fetchCount > 9 ? fetchCount : '0' + fetchCount}</span>次
|
31
|
+
</div>
|
32
|
+
</div>
|
33
|
+
<div className="flex flex-row h-8">
|
34
|
+
<span className="text-indigo-700">{beforeReload}</span>次之后将刷新页面
|
35
|
+
</div>
|
36
|
+
</main>
|
37
|
+
)
|
38
|
+
}
|
39
|
+
|
40
|
+
export default Tips
|
@@ -0,0 +1,157 @@
|
|
1
|
+
'use client'
|
2
|
+
import { Fragment, useEffect, useState } from 'react'
|
3
|
+
import { Listbox, Transition } from '@headlessui/react'
|
4
|
+
import { CheckIcon, ChevronUpDownIcon } from '@heroicons/react/20/solid'
|
5
|
+
import { find as _find } from 'lodash'
|
6
|
+
|
7
|
+
type DropItem = {
|
8
|
+
id: number | string
|
9
|
+
name: string
|
10
|
+
icon?: string
|
11
|
+
} & Record<string, any>
|
12
|
+
|
13
|
+
interface IDropListBoxProps {
|
14
|
+
domID?: string
|
15
|
+
title?: string
|
16
|
+
itemList: Array<DropItem>
|
17
|
+
selectedIndex?: number
|
18
|
+
|
19
|
+
callback?: (item: DropItem) => void
|
20
|
+
}
|
21
|
+
export default function DropListBox({ domID, title, itemList, selectedIndex, callback }: IDropListBoxProps) {
|
22
|
+
const [selected, setSelected] = useState(itemList[selectedIndex || 0])
|
23
|
+
|
24
|
+
const handleSelect = (item: DropItem) => {
|
25
|
+
setSelected(item)
|
26
|
+
if (callback) callback(item)
|
27
|
+
}
|
28
|
+
|
29
|
+
// useEffect(() => {
|
30
|
+
// let newItem: DropItem
|
31
|
+
// if (selectedIndex !== undefined) {
|
32
|
+
// newItem = itemList[selectedIndex]
|
33
|
+
// } else {
|
34
|
+
// newItem = itemList[0]
|
35
|
+
// }
|
36
|
+
// setSelected(newItem)
|
37
|
+
// if (callback) callback(newItem)
|
38
|
+
// }, [itemList, selectedIndex])
|
39
|
+
|
40
|
+
// useEffect(() => {
|
41
|
+
// console.log(`selectedIndex`, selectedIndex)
|
42
|
+
// // 当之前的selected对象在新的itemList中存在时,不做处理
|
43
|
+
// if(!_find(itemList, _item=>{
|
44
|
+
// return _item.id == selected.id && _item.name == selected.name
|
45
|
+
// })){
|
46
|
+
// let newItem: DropItem
|
47
|
+
// if (selectedIndex !== undefined && itemList[selectedIndex]) {
|
48
|
+
// newItem = itemList[selectedIndex]
|
49
|
+
// }else{
|
50
|
+
// newItem = itemList[0]
|
51
|
+
// }
|
52
|
+
// setSelected(newItem)
|
53
|
+
// if (callback) callback(newItem)
|
54
|
+
// }
|
55
|
+
// }, [itemList])
|
56
|
+
|
57
|
+
useEffect(() => {
|
58
|
+
if (selectedIndex && itemList[selectedIndex]) {
|
59
|
+
setSelected(itemList[selectedIndex])
|
60
|
+
}
|
61
|
+
}, [selectedIndex])
|
62
|
+
|
63
|
+
useEffect(() => {
|
64
|
+
// 表示itemList已经变更
|
65
|
+
if (
|
66
|
+
!_find(itemList, _item => {
|
67
|
+
return _item.id == selected.id && _item.name == selected.name
|
68
|
+
})
|
69
|
+
) {
|
70
|
+
let newItem: DropItem = (selectedIndex !== undefined ? itemList[selectedIndex] : itemList[0]) || itemList[0]
|
71
|
+
setSelected(newItem)
|
72
|
+
}
|
73
|
+
}, [itemList])
|
74
|
+
|
75
|
+
return (
|
76
|
+
<Listbox value={selected} onChange={handleSelect}>
|
77
|
+
{({ open }) => (
|
78
|
+
<>
|
79
|
+
{title ? (
|
80
|
+
<Listbox.Label className="block text-sm font-medium leading-6 text-gray-900">
|
81
|
+
{title}
|
82
|
+
</Listbox.Label>
|
83
|
+
) : null}
|
84
|
+
<div className="relative mt-2">
|
85
|
+
<Listbox.Button
|
86
|
+
id={domID || ''}
|
87
|
+
className="relative w-full cursor-default rounded-md bg-white py-1.5 pl-3 pr-10 text-left text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 sm:text-sm sm:leading-6"
|
88
|
+
>
|
89
|
+
<span className="flex items-start">
|
90
|
+
{selected?.icon ? (
|
91
|
+
<img src={selected.icon} alt="" className="h-5 w-5 flex-shrink-0 rounded-full" />
|
92
|
+
) : null}
|
93
|
+
<span className=" block truncate h-6">{selected.name}</span>
|
94
|
+
</span>
|
95
|
+
<span className="pointer-events-none absolute inset-y-0 right-0 ml-3 flex items-center pr-2">
|
96
|
+
<ChevronUpDownIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
|
97
|
+
</span>
|
98
|
+
</Listbox.Button>
|
99
|
+
|
100
|
+
<Transition
|
101
|
+
show={open}
|
102
|
+
as={Fragment}
|
103
|
+
leave="transition ease-in duration-100"
|
104
|
+
leaveFrom="opacity-100"
|
105
|
+
leaveTo="opacity-0"
|
106
|
+
>
|
107
|
+
<Listbox.Options className="absolute z-10 mt-1 max-h-56 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
|
108
|
+
{itemList.map(item => (
|
109
|
+
<Listbox.Option
|
110
|
+
key={item.id}
|
111
|
+
className={({ active }) =>
|
112
|
+
`${
|
113
|
+
active ? 'bg-indigo-600 text-white' : 'text-gray-900'
|
114
|
+
} relative cursor-default select-none py-2 pl-3 pr-9`
|
115
|
+
}
|
116
|
+
value={item}
|
117
|
+
>
|
118
|
+
{({ selected, active }) => (
|
119
|
+
<>
|
120
|
+
<div className="flex items-center">
|
121
|
+
{item.icon ? (
|
122
|
+
<img
|
123
|
+
src={item.icon}
|
124
|
+
alt=""
|
125
|
+
className="h-5 w-5 flex-shrink-0 rounded-full"
|
126
|
+
/>
|
127
|
+
) : null}
|
128
|
+
<span
|
129
|
+
className={`${
|
130
|
+
selected ? 'font-semibold' : 'font-normal'
|
131
|
+
} 'ml-3 block truncate`}
|
132
|
+
>
|
133
|
+
{item.name}
|
134
|
+
</span>
|
135
|
+
</div>
|
136
|
+
|
137
|
+
{selected ? (
|
138
|
+
<span
|
139
|
+
className={`${
|
140
|
+
active ? 'text-white' : 'text-indigo-600'
|
141
|
+
} absolute inset-y-0 right-0 flex items-center pr-4`}
|
142
|
+
>
|
143
|
+
<CheckIcon className="h-5 w-5" aria-hidden="true" />
|
144
|
+
</span>
|
145
|
+
) : null}
|
146
|
+
</>
|
147
|
+
)}
|
148
|
+
</Listbox.Option>
|
149
|
+
))}
|
150
|
+
</Listbox.Options>
|
151
|
+
</Transition>
|
152
|
+
</div>
|
153
|
+
</>
|
154
|
+
)}
|
155
|
+
</Listbox>
|
156
|
+
)
|
157
|
+
}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
const SVGPlay = ({ className }: { className?: string }) => {
|
2
|
+
const svgTest = `
|
3
|
+
<svg width="64" height="64" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
4
|
+
<path fill="#ffffff" d="M2.93 17.07A10 10 0 1 1 17.07 2.93A10 10 0 0 1 2.93 17.07zm12.73-1.41A8 8 0 1 0 4.34 4.34a8 8 0 0 0 11.32 11.32zM7 6l8 4l-8 4V6z"/>
|
5
|
+
</svg>
|
6
|
+
`
|
7
|
+
|
8
|
+
const base64String = btoa(svgTest)
|
9
|
+
|
10
|
+
const imgSrc = `data:image/svg+xml;base64,${base64String}`
|
11
|
+
return <img src={imgSrc} className={className || ''} />
|
12
|
+
}
|
13
|
+
|
14
|
+
export default SVGPlay
|
package/app/favicon.ico
ADDED
Binary file
|
package/app/globals.css
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
@tailwind base;
|
2
|
+
@tailwind components;
|
3
|
+
@tailwind utilities;
|
4
|
+
|
5
|
+
:root {
|
6
|
+
--foreground-rgb: 255, 255, 255;
|
7
|
+
--background-start-rgb: 255, 255, 255;
|
8
|
+
--background-end-rgb: 255, 255, 255;
|
9
|
+
}
|
10
|
+
|
11
|
+
@media (prefers-color-scheme: dark) {
|
12
|
+
:root {
|
13
|
+
--foreground-rgb: 255, 255, 255;
|
14
|
+
--background-start-rgb: 255, 255, 255;
|
15
|
+
--background-end-rgb: 255, 255, 255;
|
16
|
+
}
|
17
|
+
}
|
18
|
+
|
19
|
+
body {
|
20
|
+
color: rgb(var(--foreground-rgb));
|
21
|
+
background: linear-gradient(
|
22
|
+
to bottom,
|
23
|
+
transparent,
|
24
|
+
rgb(var(--background-end-rgb))
|
25
|
+
)
|
26
|
+
rgb(var(--background-start-rgb));
|
27
|
+
}
|
package/app/layout.tsx
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
import './globals.css'
|
2
|
+
import type { Metadata } from 'next'
|
3
|
+
|
4
|
+
export const metadata: Metadata = {
|
5
|
+
title: 'iPhoneOrder',
|
6
|
+
description: 'iPhoneOrder',
|
7
|
+
}
|
8
|
+
|
9
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
10
|
+
return (
|
11
|
+
<html lang="en">
|
12
|
+
<body className={''}>{children}</body>
|
13
|
+
</html>
|
14
|
+
)
|
15
|
+
}
|
package/app/page.tsx
ADDED